apigateway——Kong介绍(一)

网关

一切网络的流量都需要经过网关,api网关就是所有api请求都需要经过网关。

作用

对api进行一个统一的管理及处理,让业务更加专注于业务。

整合内部接口,统一开放入口:

image——>image

对接不同客户:

image

天然的,我们可以在网关层就处理一些非业务强关联的事务,主要就涉及认证,安全,日志,流控,接口聚合,熔断处理等一系列服务能力。

Kong

kong就是一个基于nginx强力的http接入服务器,使用lua进行插件式高扩展能力,高性能的一个API网关。社区版通过插件方式提供了如下的能力,并可以通过lua编写插件的方式进行扩展:

image

image

安装

百闻不如一见,适用的情况下,我们就用最简便的方式,docker容器进行安装,快速高效,

在一个docker环境电脑中就行安装,步骤:

1.新建docker-compose.yml:内容下
version: '2.2'

services:

  kong-database:
    image: postgres:9.4-alpine
    container_name: kong-database
    environment:
      - POSTGRES_USER=kong
      - POSTGRES_DB=kong
    healthcheck:
      test: "pg_isready -U kong && psql -d kong -U kong -c \"SELECT 1=1\""
      interval: 10s
      timeout: 5s
      retries: 5

  kong-migration:
    image: kong:${KONG_VERSION}
    container_name: kong-migration
    depends_on:
      kong-database:
        condition: service_healthy
    environment:
      - KONG_DATABASE=postgres
      - KONG_PG_HOST=kong-database
    command: sh -c "kong migrations up && touch migrations_run && sleep 30"
    healthcheck:
      test: "if [[ -f migrations_run ]] ; then exit 0; else exit 1; fi"
      interval: 10s
      timeout: 5s
      retries: 5

  kong:
    image: kong:${KONG_VERSION}
    container_name: kong
    depends_on:
      kong-migration:
        condition: service_healthy
    healthcheck:
      test: "kong health"
      interval: 10s
      timeout: 5s
      retries: 5
    environment:
      - KONG_DATABASE=postgres
      - KONG_PG_HOST=kong-database
      - KONG_ADMIN_LISTEN=0.0.0.0:8001
    ports:
      - 8001:8001
      - 8000:8000

  kong-dashboard:
    image: pgbi/kong-dashboard
    container_name: kong-dashboard
    ports:
      - 8080:8080
    depends_on:
      kong:
        condition: service_healthy
    entrypoint: ./docker/entrypoint.sh start --kong-url http://kong:8001 
2.启动
KONG_VERSION=0.11-alpine  docker-compose -f docker-compose.yml up
3.分析

在这里我们没有进行集群,一共启动三个服务,kong的数据库,kong网关,还有一个kong的管理应用,可以直观的提供web界面浏览kong网关相关信息。我们来看看提供出来的相关参数。

端口

kong:8000业务端口(所有api进此端口),8001管理端口(kong提供了restful管理接口,可对api,插件进行各种管理操作,如新增接口,新增用户等)。

kong的数据库可以使用postgres,也可以使用cassandra。

jenkins持续集成二——spring boot集成设置

前面安装好了环境,这一篇主要讲下设置

系统管理->管理插件

image_thumb9

以下是默认安装插件之外,需要额外安装的插件。

系统管理->全局工具配置

image_thumb4

配置JDK

image_thumb1

配置git

image_thumb3

配置maven

image_thumb7

系统管理->系统设置

image_thumb11

这里主要需要对jenkins 将集成应用通过ssh部署到应用服务器的SSH 连接配置

示例如下,同时需要了解SSH免密登录相关内容。

image_thumb14

SSH免密登录

#ubuntu环境在客户机器上生成密钥对,密钥会生成在~/.ssh/目录下
ssh-keygen -t rsa

生成多机器不同密钥对免密登录配置

#配置多远程机器不同密钥的配置,在~/.ssh/ 生成config
touch config

示例配置如下

image_thumb16

将公钥拷贝到远程机器上

#ssh-copy-id [-i [identity_file]] [user@]machine
ssh-copy-id -i id_rsa_git git@127.0.0.1
#该公钥信息会记录到git 服务器的~/.ssh/authorized_keys 文件

测试

ssh git@git #or ssh git@127.0.0.1

Credentials

从git 拉取代码,使用SSH 信用连接,设置 Credentials,添加git 相关的SSH 连接认证,如下图示例:

image_thumb20

Maven 构建项目配置

新建按钮如下

image_thumb18

配置

git源

image_thumb22

构建触发

image_thumb24

10分钟构建一次

自动部署

image_thumb26

文件存档

image_thumb28

jenkins持续集成(一)-基础安装设置

jenkins  作为持续集成现在用得越来越广,以前都是公司直接配置好了用上就好,现在就按部就班从0开始弄个持续集成环境吧。


jenkins

安装

wget -q -O - https://jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins

启动

启动:  
sudo service jenkins start  
停止:  
sudo service jenkins stop

路径

log 路径:/var/log/jenkins/jenkins.log
jenkins的home路径:/var/lib/jenkins
端口号修改路径:sudo vi /etc/default/jenkins

初始化

通过浏览器就可以访问Jenkins了。比如我的地址:http://192.168.2.126:7777/,按照页面提示安装插件及设置管理员账户密码。

image

为了确保Jenkins的安全,将管理员的密码写入文件,需要复制到下面的文本框做验证。

/var/lib/jenkins/secrets/initialAdminPassword

然后,到了选择插件的界面,通过附加功能扩展Jenkins可以支持许多不同的需求。

image

image

image

image

git

安装

安装
sudo apt-get install git
#添加用户
useradd git
#配置SSH登录
cd ~
mkdir .ssh 
cd .ssh
touch authorized_keys
cat ~/id_rsa.pub >> ~/.ssh/authorized_keys
rm ~/id_rsa.pub
#授权
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh

使用

#新建目录
mkdir   zwr
#新建初始化仓库
git init --bare test.git

Maven

安装

方式一

#下载
wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz
#解压
tar -xzf apache-maven-3.3.9-bin.tar.gz
#移动
mv apache-maven-3.3.9  /usr/share/maven
#设置环境变量
vim /etc/profile
#环境变量内容
M2_HOME=/usr/share/maven
CLASSPATH=$CLASSPATH:$M2_HOME/lib
PATH=$PATH:$M2_HOME/bin
export   PATH    CLASSPATH   M2_HOME
#生效
source /etc/profile

方式二

apt-get install -y maven
mvn -version

如何架构一个新的综合金融交易系统

 

技术产品需求

客户端网站系统(app),业务系统(接口),运维平台

网站系统:

基础系统:

1.用户(帐户)管理系统 2.商品(行情)管理系统 3.订单(交易管理)系统 4.支付系统5.风控系统

增值系统:

客户服务系统,筛选筛选系统,资讯系统。。。

业务系统:

管理系统:

用户管理系统

运营支撑系统

核心系统:

订单交易系统

查询系统

行情系统

增值系统:

数据统计(报表)系统

客户服务系统

 

技术栈选型

1.前端系统

2.后端系统

2.1 负载均衡及http 服务器接入:nginx

2.2 web 应用容器 SOA 服务 Tomcat + dubbo

2.3 文件服务器

        2.3.1 储存方式
        2.3.2 储存容量
        2.3.3 安全性与存取权限控管
        2.3.4 存取效能

2.4 缓存服务器
         2.4.1 分布式Redis缓存
         2.4.2 Memcache缓存

2.5 消息系统
         2.5.1 ActiveMQ
         2.5.2 分布式消息系统Kafka、Rocketmq等

2.6 数据持久层
         2.6.1 关系型数据库
              (1). Mysql

        2.6.2 Nosql
              (1). MongoDB

mysql优化系列(一)

使用新数据库,玩一段时间后,我想大家就应该会碰到一系列的小问题,那是正常的,因为就像处女朋友一样,作为一个负责的男人,不能随便玩一玩了事。

当然,本人也不是泡妞高手,所以也需要不断学习充电,当然老毛子说理论实践是要结合的,在此留下一记。

    1.全表扫描

查询一定要避免全表扫描,如果你有前任,不管她的名字是不是叫oracle,你一定吃过全表扫描的亏,如果很不幸,你一直是只单身狗,那么你也不要沮丧,这里有现成的理论,单身狗们快拿去实践一下:

  • 在查询条件字段建立索引,这是最基本的常识,如果你不记牢你女朋友的电话号码,你大冬天的每次都去她楼下找她么!当然索引也不是越多越好,就像衣服穿多了,你何时才能上三垒!insert update 看到索引就说不要不要的!
  • 在查询条件中,不要拿null值进行判断,既然这样,如果可以,请把你的字段都设置成非NULL,跟默认值进行判断,0 就是0,NULL是什么鬼,你女朋友一定会河东狮吼!
  • 对于 like 这类关键字,大家也尽量少用为好,不要问我为什么,你女朋友真的要大海捞针!
  • 不要给你女朋友太多的选择条件,直接告诉她怎么做,所以 or 这种条件字段也尽量不要用了!
  • 如果字段可以使用 int 类型,那么就不要使用 字符串类型,你女朋友的脑子有时候没有你想象中的那么好,你把字符串给她一个个对比,你们约好的出门时间应该又要推迟了,电影也只能看后半场了,所以给她处理的东西也越简单越好.

当然,mysql 既然是你的新女朋友,那么她也会有自己的一些特性,请在查询的时候限制她查询的范围,limit n,你只需要 n个记录,那么就不要让她找出 n+m来,不然她真的会这么干!

有时候,即使你给了限制,还是会出错!

请看

select * from order where kj_customer_acco =

@kj_customer_acco and position_int > @position_int order by position_int asc limit 0,@request_num

上面给出的示范,除了

select *

这个是不好的外(增加多余的网络传输压力等),其他应该都是几乎完美的一个语句,但是就是如此,有时候你女朋友还是爽约了,那一天你在雨中苦等了2小时,以为会有一场雨中浪漫的约会,结果是你淋成了落汤鸡!因为你没有从实际国情出发,我们是一国两制的社会,胡乱套用也会水土不服,所以我们一定要具体问题具体分析,那么就先来看下国情:

CREATE TABLE order
(
	position_int                   bigint          NOT NULL AUTO_INCREMENT,
	kj_customer_acco               varchar(10)     DEFAULT ' '        ,
	update_date                    int             DEFAULT 0          ,
	update_time                    int             DEFAULT 0          ,
	PRIMARY KEY(position_int)
);
CREATE INDEX idx_position_int ON order(position_int ASC );

然后我们造了测试数据进去

begin
    declare i int default 1000;
    
	while i < 1300 do
    BEGIN
    declare j int default 1;
		while j < 9060 do
       insert into `order` values(DEFAULT,i,20151203,111111);
       set j=j+1;
    end while; 
    set i=i+1;
    END;
   end while;
   commit;
end

我们使用上面的语句对这个表进行翻页式查询,在查询中,每当如下语句

select * from order where kj_customer_acco = @kj_customer_acco  and position_int > @position_int

查询出来的数据不足 @request_num,的时候,就会特别的慢,使用 EXPLAIN 关键字查看下执行计划

image

我们发现查询搜索的边界在我们的意料之外,它的搜索范围是1231780行数据,当剩余查询的数据条数大于@request_num,因为我们加了limit 的限制,所以查询搜索到@request_num 条数据的时候就结束搜索了,但是当剩余查询的有效数据量不足@request_num时,就会继续往下查询把rows 条记录都会搜一遍!

spring_mvc_mybatis傻瓜入门篇之二spring与spring-mvc

上篇我们已经弄好了spring 与mybatis,即搞定了数据model层的东西,已经可以对外提供数据服务了,那么现在就要将spring-mvc加上去,增加逻辑与展示!

一.增加依赖包

在pom.xml中增加配置项

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency>

增加spring_mvc 的配置文件(servlet-context.xml)如下

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<!-- SpringMVC上传文件时,需要配置MultipartResolver处理器 -->
	<beans:bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<beans:property name="defaultEncoding" value="utf-8" />
		<beans:property name="maxUploadSize" value="10485760000" />
		<beans:property name="maxInMemorySize" value="40960" />
	</beans:bean>
    
	<context:component-scan base-package="com.test.controller" />
	
</beans:beans>

将该 配置文件路径添加到web.xml上:

<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

配置好了,然后就可以开始新建个页面测试下了

新建展示页面showUserInfo.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
${userinfo.name}:${userinfo.passwd}
</body>
</html>

新建逻辑控制页面代码:

package com.test.controller;

import javax.annotation.Resource;  
import javax.servlet.http.HttpServletRequest;  
import org.springframework.stereotype.Controller;  
import org.springframework.ui.Model;  
import org.springframework.web.bind.annotation.RequestMapping;  
import com.test.model.UserInfo;  
import com.test.service.IUserInfoService;  


@Controller  
@RequestMapping("/user") 
public class queryUserController {

	 @Resource  
	 private IUserInfoService userService;  
	  
	    @RequestMapping("/showUserInfo")  
	    public String toIndex(HttpServletRequest request, Model model) {  
	        long pos_int = Long.parseLong(request.getParameter("id"));  
	        UserInfo userinfo = this.userService.getUserInfoById(pos_int);  
	        model.addAttribute("userinfo", userinfo);  
	        return "showUserInfo"; 
	    }
}

好了,就可以运行调试:http://localhost:8080/smvc/user/showUserInfo?id=1

QA:

有很大几率你是不能一次性就运行成功的,会报很多错误,那么耐性看他报错信息,然后找谷歌问一问你就可以得到答案!当然如果你怕麻烦,那么也可以预先做下如下处理以避免各种报错:

1.在项目属性中,添加如下图红框中的路径配置

image

其他的一时也记不起来了,出问题了百度吧,或者留言!

Linux IO模式及 select、poll、epoll详解(转载)

这是目前入门级别感觉比较清晰又详尽的IO模式讲解,值得一看。

注:本文是对众多博客的学习和总结,可能存在理解错误。请带着怀疑的眼光,同时如果有错误希望能指出。

同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的。所以先限定一下本文的上下文。

本文讨论的背景是Linux环境下的network IO。

一 概念说明

在进行解释之前,首先要说明几个概念:
– 用户空间和内核空间
– 进程切换
– 进程的阻塞
– 文件描述符
– 缓存 I/O

用户空间与内核空间

现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。

进程切换

为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。

从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:
1. 保存处理机上下文,包括程序计数器和其他寄存器。
2. 更新PCB信息。
3. 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
4. 选择另一个进程执行,并更新其PCB。
5. 更新内存管理的数据结构。
6. 恢复处理机上下文。

注:总而言之就是很耗资源,具体的可以参考这篇文章:进程切换

进程的阻塞

正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的

文件描述符fd

文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

缓存 I/O

缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

缓存 I/O 的缺点:
数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

二 IO模式

刚才说了,对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:
1. 等待数据准备 (Waiting for the data to be ready)
2. 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

正式因为这两个阶段,linux系统产生了下面五种网络模式的方案。

同步:
– 阻塞 I/O(blocking IO)
– 非阻塞 I/O(nonblocking IO)
– I/O 多路复用( IO multiplexing)
– 信号驱动 I/O( signal driven IO)

异步:
– 异步 I/O(asynchronous IO)

注:由于signal driven IO在实际中并不常用,所以我这只提及剩下的四种IO Model。

阻塞 I/O(blocking IO)

在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:

当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

所以,blocking IO的特点就是在IO执行的两个阶段都被block了。

非阻塞 I/O(nonblocking IO)

linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:

当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。

所以,nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。

I/O 多路复用( IO multiplexing)

IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。

当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。

这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。

所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)

在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。

信号驱动式U/O模型:

         可以用信号,让内核在描述符就绪时发送SIGIO信号通知我们。称为信号驱动式I/O

        我们首先开启套接字的信号驱动式I/O功能,并通过sigaction系统调用安装一个信号处理函数。该系统调用将立即返回,我们的进程继续工作,也就是说它没有被阻塞。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已准备好待处理。也可以立即通知循环,让它读取数据报。

         无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达期间进程不被阻塞。主循环可以继续执行,只要等待来自信号处理函数的通知:既可以是数据已准备好被处理,也可以是数据报已准备好被读取。

异步 I/O(asynchronous IO)

inux下的asynchronous IO其实用得很少。先看一下它的流程:

用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

总结

blocking和non-blocking的区别

调用blocking IO会一直block住对应的进程直到操作完成,而non-blocking IO在kernel还准备数据的情况下会立刻返回。

synchronous IO和asynchronous IO的区别

在说明synchronous IO和asynchronous IO的区别之前,需要先给出两者的定义。POSIX的定义是这样子的:
– A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;
– An asynchronous I/O operation does not cause the requesting process to be blocked;

两者的区别就在于synchronous IO做”IO operation”的时候会将process阻塞。按照这个定义,之前所述的blocking IO,non-blocking IO,IO multiplexing都属于synchronous IO。

有人会说,non-blocking IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO operation”是指真实的IO操作,就是例子中的recvfrom这个system call。non-blocking IO在执行recvfrom这个system call的时候,如果kernel的数据没有准备好,这时候不会block进程。但是,当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内,进程是被block的。

而asynchronous IO则不一样,当进程发起IO 操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。

各个IO Model的比较如图所示:

通过上面的图片,可以发现non-blocking IO和asynchronous IO的区别还是很明显的。在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

三 I/O 多路复用之select、poll、epoll详解

select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。(这里啰嗦下)

select

int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但 是这样也会造成效率的降低。

poll

int poll (struct pollfd *fds, unsigned int nfds, int timeout);

不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。

struct pollfd { int fd; /* file descriptor */ short events; /* requested events to watch */ short revents; /* returned events witnessed */ };

pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。

从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

epoll

epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

一 epoll操作过程

epoll操作过程需要三个接口,分别如下:

int epoll_create(int size);//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

1. int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大,这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值,参数size并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议
当创建好epoll句柄后,它就会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
函数是对指定描述符fd执行op操作。
– epfd:是epoll_create()的返回值。
– op:表示op操作,用三个宏来表示:添加EPOLL_CTL_ADD,删除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。分别添加、删除和修改对fd的监听事件。
– fd:是需要监听的fd(文件描述符)
– epoll_event:是告诉内核需要监听什么事,struct epoll_event结构如下:

struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; //events可以是以下几个宏的集合: EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭); EPOLLOUT:表示对应的文件描述符可以写; EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来); EPOLLERR:表示对应的文件描述符发生错误; EPOLLHUP:表示对应的文件描述符被挂断; EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。 EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待epfd上的io事件,最多返回maxevents个事件。
参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

二 工作模式

epoll对文件描述符的操作有两种模式:LT(level trigger)ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:
LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。

1. LT模式

LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的。

2. ET模式

ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)

ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

3. 总结

假如有这样一个例子:
1. 我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符
2. 这个时候从管道的另一端被写入了2KB的数据
3. 调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作
4. 然后我们读取了1KB的数据
5. 调用epoll_wait(2)……

LT模式:
如果是LT模式,那么在第5步调用epoll_wait(2)之后,仍然能受到通知。

ET模式:
如果我们在第1步将RFD添加到epoll描述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait(2)之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候 ET 工作模式才会汇报事件。因此在第5步的时候,调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。

当使用epoll的ET模型来工作时,当产生了一个EPOLLIN事件后,
读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取:

while(rs){ buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0); if(buflen < 0){ // 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读 // 在这里就当作是该次事件已处理处. if(errno == EAGAIN){ break; } else{ return; } } else if(buflen == 0){ // 这里表示对端的socket已正常关闭. } if(buflen == sizeof(buf){ rs = 1; // 需要再次读取 } else{ rs = 0; } }

Linux中的EAGAIN含义

Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。
从字面上来看,是提示再试一次。这个错误经常出现在当应用程序进行一些非阻塞(non-blocking)操作(对文件或socket)的时候。

例如,以 O_NONBLOCK的标志打开文件/socket/FIFO,如果你连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返回,read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。
又例如,当一个系统调用(比如fork)因为没有足够的资源(比如虚拟内存)而执行失败,返回EAGAIN提示其再调用一次(也许下次就能成功)。

三 代码演示

下面是一段不完整的代码且格式不对,意在表述上面的过程,去掉了一些模板代码。

#define IPADDRESS "127.0.0.1" #define PORT 8787 #define MAXSIZE 1024 #define LISTENQ 5 #define FDSIZE 1000 #define EPOLLEVENTS 100 listenfd = socket_bind(IPADDRESS,PORT); struct epoll_event events[EPOLLEVENTS]; //创建一个描述符 epollfd = epoll_create(FDSIZE); //添加监听描述符事件 add_event(epollfd,listenfd,EPOLLIN); //循环等待 for ( ; ; ){ //该函数返回已经准备好的描述符事件数目 ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1); //处理接收到的连接 handle_events(epollfd,events,ret,listenfd,buf); } //事件处理函数 static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf) { int i; int fd; //进行遍历;这里只要遍历已经准备好的io事件。num并不是当初epoll_create时的FDSIZE。 for (i = 0;i < num;i++) { fd = events[i].data.fd; //根据描述符的类型和事件类型进行处理 if ((fd == listenfd) &&(events[i].events & EPOLLIN)) handle_accpet(epollfd,listenfd); else if (events[i].events & EPOLLIN) do_read(epollfd,fd,buf); else if (events[i].events & EPOLLOUT) do_write(epollfd,fd,buf); } } //添加事件 static void add_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev); } //处理接收到的连接 static void handle_accpet(int epollfd,int listenfd){ int clifd; struct sockaddr_in cliaddr; socklen_t cliaddrlen; clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen); if (clifd == -1) perror("accpet error:"); else { printf("accept a new client: %s:%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port); //添加一个客户描述符和事件 add_event(epollfd,clifd,EPOLLIN); } } //读处理 static void do_read(int epollfd,int fd,char *buf){ int nread; nread = read(fd,buf,MAXSIZE); if (nread == -1) { perror("read error:"); close(fd); //记住close fd delete_event(epollfd,fd,EPOLLIN); //删除监听 } else if (nread == 0) { fprintf(stderr,"client close.\n"); close(fd); //记住close fd delete_event(epollfd,fd,EPOLLIN); //删除监听 } else { printf("read message is : %s",buf); //修改描述符对应的事件,由读改为写 modify_event(epollfd,fd,EPOLLOUT); } } //写处理 static void do_write(int epollfd,int fd,char *buf) { int nwrite; nwrite = write(fd,buf,strlen(buf)); if (nwrite == -1){ perror("write error:"); close(fd); //记住close fd delete_event(epollfd,fd,EPOLLOUT); //删除监听 }else{ modify_event(epollfd,fd,EPOLLIN); } memset(buf,0,MAXSIZE); } //删除事件 static void delete_event(int epollfd,int fd,int state) { struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev); } //修改事件 static void modify_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev); } //注:另外一端我就省了

四 epoll总结

在 select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一 个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait() 时便得到通知。(此处去掉了遍历文件描述符,而是通过监听回调的的机制。这正是epoll的魅力所在。)

epoll的优点主要是一下几个方面:
1. 监视的描述符数量不受限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。select的最大缺点就是进程打开的fd是有数量限制的。这对 于连接数量比较大的服务器来说根本不能满足。虽然也可以选择多进程的解决方案( Apache就是这样实现的),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。

  1. IO的效率不会随着监视fd的数量的增长而下降。epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数。

如果没有大量的idle -connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当遇到大量的idle- connection,就会发现epoll的效率大大高于select/poll。

Java nio和多路复用

java 1.4 nio提供的select,这是一种多路复用I/O(multiplexed non-blocking I/O)模型,底层是使用select或者poll。I/O复用就是,阻塞在select或者poll系统调用的某一个之上,而不是阻塞在真正的I/O系统调用之上。JDK 5.0 update 9和JDK 6.0在linux下支持使用epoll,可以提高并发idle connection的性能(http://blogs.sun.com/alanb/entry/epoll)。
“BIO是指阻塞IO方式,即读和写必须为同步方式,NIO是指异步(用户进程未被IO阻塞)读,同步(用户进程被IO阻塞)写的方式,AIO是指异步读,异步写的方式。
在网络协议上java对于TCP/IP和UDP/IP均支持,在网络IO的操作上,目前java仅支持BIO和NIO两种方式。”

(读:读取内核状态是否准备好写:数据内核态->用户态的拷贝

Reactor 和 Proactor

这是目前网络编程中经常用到两种事件驱动的设计模式。

其实结合上面来看,reactor 模式思想就是基于非阻塞同步IO的方式,也即I/O 多路复用。

其流程如下:

  • 步骤 1) 等待事件 (Reactor 的工作)
  • 步骤 2) 发”已经可读”事件发给事先注册的事件处理者或者回调 ( Reactor 要做的)
  • 步骤 3) 读数据 (用户代码要做的)
  • 步骤 4) 处理数据 (用户代码要做的)

而proactor模式思想基于完全异步IO方式。

  • 步骤 1) 等待事件 (Proactor 的工作)
  • 步骤 2) 读数据(看,这里变成成了让 Proactor 做这个事情)
  • 步骤 3) 把数据已经准备好的消息给用户处理函数,即事件处理者(Proactor 要做的)
  • 步骤 4) 处理数据 (用户代码要做的)

区别就在第三步骤,所以如果我们的网络库使用了Reactor设计模式,使用的是IO复用,那么可以通过封装第三个步骤,变成proactor 模式,这样可以让用户进程对读数据这一步无感知,那么使用了 Proactor设计模式封装过的IO多路复用模型在使用上与异步IO模型无差异,为什么这样做,虽然不会提升我们系统的性能,我们的服务在跨平台的时候可以让用户代码脱离 IO模型可能不一样的限制,降低耦合,提高生产率。

参考

用户空间与内核空间,进程上下文与中断上下文[总结]
进程切换
维基百科-文件描述符
Linux 中直接 I/O 机制的介绍
IO – 同步,异步,阻塞,非阻塞 (亡羊补牢篇)
Linux中select poll和epoll的区别
IO多路复用之select总结
IO多路复用之poll总结
IO多路复用之epoll总结

(转载自:https://segmentfault.com/a/1190000003063859,作者:人云思云

spring_mvc_mybatis傻瓜入门篇之spring与mybatis

  • 一.介绍

  • spring
  • 目前编程,一般我们都会在一个框架一下进行编程,而目前J2EE比较流行的框架,SPRING 当居其首。当然我们可以先撇开JAVA,撇开SPRING,一般进行编程都会运用到GOF中设计模式思想使得我们的代码程序可读可扩展解耦等等,那么在设计框架的时候,不管啥语言啥框架,他的中心思想就是屏蔽细节让我们用起来简单方便,实现这个目标有很多方式,那么作为GOD设计模式的延伸,一般的服务框架目前都已经运用IOC->AOP->SOA思想进行框架的设计实现!最基础的是 IOC,在此基础上实现AOP,SOA!关于其中的关系传送门在此!SPIRNG只是众多框架中的一种,但好用方便,所以用户众多!

  • SPRING_MVC

  • MVC是一种3层设计模式,web mvc 顾名思义就是web端的mvc,SPRING_MVC是对MVC的一种web实现!

  • Mybatis

  • mybaits封装了后台与数据库持久层的一个框架!

相互关系

spring作为核心可以自由的与各种持久层框架,web框架组合出不同的架构,如ssh(web框架struct,持久层框架hiberante)等!所以其他都可换,核心 spring 就不要换了,一个好的芯还是很重要的! 今天要入门的就是 spring + spring-mvc+mybatis

二.工具安装

工欲善其事必先利其器,spring 的开发都会有一个 sts的套件,其实 就是帮你安装好方便使用spring 相关插件的 eclipse !那就下载过来安装起来就好了!

三.创建项目

我们项目使用maven 管理,如果你还不知道maven ,那么找wikipedia 详细了解下,如果你只想知道在eclipse下的安装使用,那么这里你可以快速入门。 安装好之后之后,就可以建立maven工程,按说明next你就成功了:

image

通过pom.xml 配置文件引入各种需要的jar包具体看下面各个模块的需要引入!一般我们会引入日志,所以再配置下日志的配置文件如下(log4j.xml):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

	<!-- Appenders -->
	<appender name="console" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%-5p: %c - %m%n" />
		</layout>
	</appender>
	
	<!-- Application Loggers -->
	<logger name="com.hundsun.smvc">
		<level value="info" />
	</logger>
	
	<!-- 3rdparty Loggers -->
	<logger name="org.springframework.core">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.beans">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.context">
		<level value="info" />
	</logger>

	<logger name="org.springframework.web">
		<level value="info" />
	</logger>

	<!-- Root Logger -->
	<root>
		<priority value="warn" />
		<appender-ref ref="console" />
	</root>
	
</log4j:configuration>

四 .spring

spring 框架只需要在 pom.xml上配置引入spring 的jar,如下

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<groupId>com.hundsun</groupId>
	<artifactId>smvc</artifactId>
	<name>smvc</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.6</java-version>
		<org.springframework-version>4.2.0.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
	<dependencies>
	<!-- Spring -->
	<!-- spring核心包 -->  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-core</artifactId>  
        <version>${org.springframework-version}</version>  
    </dependency>  
  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-web</artifactId>  
        <version>${org.springframework-version}</version>  
    </dependency>  
  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-oxm</artifactId>  
        <version>${org.springframework-version}</version>  
    </dependency>  
  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-tx</artifactId>  
        <version>${org.springframework-version}</version>  
    </dependency>  
  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-jdbc</artifactId>  
        <version>${org.springframework-version}</version>  
    </dependency>  
  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-webmvc</artifactId>  
        <version>${org.springframework-version}</version>  
    </dependency>  
  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-aop</artifactId>  
        <version>${org.springframework-version}</version>  
    </dependency>  
  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-context-support</artifactId>  
        <version>${org.springframework-version}</version>  
    </dependency>  
  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-test</artifactId>  
        <version>${org.springframework-version}</version>  
    </dependency>  
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				 </exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

	</dependencies>
</project>

五.在Spring中加入mybatis

先在pom.xml配置上mybatis 用到的引入包信息

<!-- jackson 包 -->
    <dependency>  
        <groupId>org.codehaus.jackson</groupId>  
        <artifactId>jackson-mapper-asl</artifactId>  
        <version>1.9.13</version>  
    </dependency>
		
		<!-- mybatis 包 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.2.8</version>
		</dependency>

		<!--mybatis spring 插件 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.2.2</version>
		</dependency>
		
		<!-- mysql连接 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.34</version>
		</dependency>
		
		<!-- dbcp的jar包,用来在applicationContext.xml中配置数据库 -->  
   		<dependency>  
           <groupId>commons-dbcp</groupId>  
           <artifactId>commons-dbcp</artifactId>  
           <version>1.2.2</version>  
   		</dependency>

配置jdbc 配置文件 (jdbc.properties)

url=jdbc:mysql://115.29.151.158:3306/mytrade?useUnicode=true&characterEncoding=utf8 
driver=com.mysql.jdbc.Driver
username=********
password=*******
#定义初始连接数  
initialSize=0 
#定义最大连接数  
maxActive=20  
#定义最大空闲  
maxIdle=20 
#定义最小空闲  
minIdle=1  
#定义最长等待时间  
maxWait=60000  
  • 配置spring 引入mybatis 的配置文件(spring-mybatis.xml)

  • <?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:p="http://www.springframework.org/schema/p"  
        xmlns:context="http://www.springframework.org/schema/context"  
        xmlns:mvc="http://www.springframework.org/schema/mvc"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans    
      http://www.springframework.org/schema/beans/spring-beans-3.1.xsd    
      http://www.springframework.org/schema/context    
      http://www.springframework.org/schema/context/spring-context-3.1.xsd    
      http://www.springframework.org/schema/mvc    
      http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">  
      
        <!-- 自动扫描 -->  
        <context:component-scan base-package="com.test" />  
      
        <!-- 引入配置文件 -->  
        <bean id="propertyConfigurer"  
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
            <property name="location" value="classpath:jdbc.properties" />  
        </bean>  
      
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
            destroy-method="close">  
            <property name="driverClassName" value="${driver}" />  
            <property name="url" value="${url}" />  
            <property name="username" value="${username}" />  
            <property name="password" value="${password}" />  
            <!-- 初始化连接大小 -->  
            <property name="initialSize" value="${initialSize}"></property>  
            <!-- 连接池最大数量 -->  
            <property name="maxActive" value="${maxActive}"></property>  
            <!-- 连接池最大空闲 -->  
            <property name="maxIdle" value="${maxIdle}"></property>  
            <!-- 连接池最小空闲 -->  
            <property name="minIdle" value="${minIdle}"></property>  
            <!-- 获取连接最大等待时间 -->  
            <property name="maxWait" value="${maxWait}"></property>  
        </bean>  
      
        <!-- Spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->  
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
            <property name="dataSource" ref="dataSource" />  
            <!-- 自动扫描mapping.xml文件 -->  
            <property name="mapperLocations" value="classpath:com/test/mapping/*.xml"></property>  
        </bean>  
      
        <!-- DAO接口所在包名,Spring会自动查找其下的类 -->  
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
            <property name="basePackage" value="com.test.Dao" />  
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>  
        </bean>  
      
        <!-- 事务管理 -->  
        <bean id="transactionManager"  
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
            <property name="dataSource" ref="dataSource" />  
        </bean>  
      
    </beans>  
  • 将该配置文件添加到web.xml中
    	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
    	<context-param>
    		<param-name>contextConfigLocation</param-name>
    		<param-value>
    		/WEB-INF/spring/root-context.xml;
    		classpath:spring-mybatis.xml;
    		</param-value>
    到这里,我们需要的包都已经添加好了,然后我们就需要测试下我们的spring 通过mybatis与mysql 数据库是不是通的,当然我们得先把数据库,表都准备好了,这个就不说了!接下来你们可能觉得需要写代码,写一些model层出来通过mybatis与mysql进行联通了?不需要,我们只要借助一个mybatis中的神器 mybatis-generator 用来自动生成代码,当然你需要把mysql 的数据连接地址,需要生成的model 的表等一些信息配置起来!具体还是看前面链接里面的教程吧,就不重复说了:自动生成以下类:
    package com.test.Dao;
    
    import com.test.model.UserInfo;
    
    public interface UserInfoMapper {
        int deleteByPrimaryKey(Long positionInt);
    
        int insert(UserInfo record);
    
        int insertSelective(UserInfo record);
    
        UserInfo selectByPrimaryKey(Long positionInt);
    
        int updateByPrimaryKeySelective(UserInfo record);
    
        int updateByPrimaryKey(UserInfo record);
    }
                      然后把接口及服务层实现写出来,就写个简单的获取用户信息吧:

                      image

                      package com.test.service;
                      
                      import com.test.model.UserInfo;
                      
                      public interface IUserInfoService {
                      	public UserInfo getUserInfoById(long pos_int);
                      }
                      
                      package com.test.service.impl;
                      
                      import javax.annotation.Resource;  
                      import org.springframework.stereotype.Service;  
                      import com.test.model.UserInfo;  
                      import com.test.Dao.UserInfoMapper;  
                      import com.test.service.IUserInfoService; 
                      
                      @Service("userinfoService") 
                      public class UserInfoServiceImpl implements IUserInfoService {
                      	@Resource  
                          private UserInfoMapper userinfoDao;  
                      	
                      	@Override  
                          public UserInfo getUserInfoById(long pos_int) {  
                              return this.userinfoDao.selectByPrimaryKey(pos_int);  
                          }  
                      }
                      

                      然后写个 jtest:

                      package smvc;
                      
                      import javax.annotation.Resource;  
                      import org.apache.log4j.Logger;  
                      import org.junit.Test;  
                      import org.junit.runner.RunWith;  
                      import org.springframework.test.context.ContextConfiguration;  
                      import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
                      import com.alibaba.fastjson.JSON;  
                      import com.test.model.UserInfo;  
                      import com.test.service.IUserInfoService;  
                      
                      
                      @RunWith(SpringJUnit4ClassRunner.class)  
                      @ContextConfiguration(locations = { "classpath:spring-mybatis.xml" })  
                      public class TestMyBatis {
                      	private static Logger logger = Logger.getLogger(TestMyBatis.class);  
                      	
                      	 @Resource  
                      	 private IUserInfoService userinfoService = null;  
                      	 
                      	 @Test  
                      	    public void test1() {  
                      	        UserInfo userinfo = userinfoService.getUserInfoById((long) 1);  
                      	        logger.info(JSON.toJSONString(userinfo));  
                      	    }  
                      }

                      运行下,我们就可以愉快的得到数据了!

                      FTP服务器及账号访问设置

                      背景

                      最近做个项目与其他系统对接,接口数据他们非要以文件格式获取,好吧,

                      按照他们的需求要弄个FTP服务器,然后他们的系统还卖了好多地方(好有钱),

                      然后他们都要来取数据,有些数据公用的,有些数据还是私有的,好吧还要整好多

                      账号起来!

                      安装

                      FTP服务器是部署在linux系统上的,所以很自然就想到了用vsftpd,其他都不多说了,

                      直接把FTP服务安装起来再说了!安装还是直接使用yum(真神器!):

                      yum install vsftpd

                      就这么简单,然后啥都装好了!然后就启动服务就可以了:

                      service vsftpd start

                      配置

                      vsftpd.conf

                      vsftpd.conf 是vsftpd的配置文件,用来控制vsftpd的各项功能。默认状态下,它的位置

                      是/etc/vsftpd.conf或者在/etc/vsftpd/vsftpd.conf。里面的配置项的含义还是需要了解一番,

                      这里我就不多做介绍,你们直接传送门走起!

                      虚拟用户

                      看完上面的配置,那么我们就来进行本次重点内容虚拟用户的配置过程:

                      基本思路就是先创建一个用户,然后在这个用户下面挂多个虚拟的用户

                      进行!