scservers(四)服务调用——接口与负载均衡

前言

前面的三篇搭起微服务的基本应用框架:服务发现,分布式动态配置,消息总线的通知能力等。就如人的身体基本上已经有了头脑四肢,基本成形。

头脑四肢之间需要互联互通,相当于服务调用,并且需要一定的协议接口及基于协议的序列化反序列化,比如dubbo,grpc甚至原生的别人不了解的协议等。目前我们这里只介绍下http的,其他基本类同。服务的调用还要负载均衡,不然只用右手,容易起茧。

http-rest

目前http协议所使用的远程调用范式基本以REST为范式,spring3之后就提供RestTemplate来执行rest http接口调用,并提供序列化反序列的能力。但没有负载均衡,负载均衡依赖于服务注册,所以springcloud 提供了Ribbon作为软负载。

而Feign,则是及大成者,包含软件负载(ribbon)的http远程调用及序列化反序列化能力。即有如下方式:

* 原生RestTemplate+Ribbon

* Feign

RestTemplate+Ribbon

  • 配置

    我们需要添加Ribbon的依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>

    但是在示例里面我们并没有在pom.xml中看到这个依赖配置,那是因为我们已经存在如下依赖:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    前面我们说过,负载还依赖于服务注册,而如果使用eureka作为服务注册,那么其已经包含了Ribbon的依赖了。如果使用其他服务注册发现中心,如consul、zookeeper、etcd,那就要加上Ribbon的依赖。嗯,全家桶确实名不虚传。

    eureka的配置这里就不再复述了。

  • 使用

    • step1:@LoadBalanced

      在RestTemplate加上注解@LoadBalanced,让RestTemplate支持负载客户端。
    @Configuration
     class MyConfiguration {
    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
    return new RestTemplate();
    }
    }
    • step2:配置负载模式

      标明Ribbon负载的工作模式,如果是轮训可以跳过这一步(默认支持轮训),如果想该用其他方式按照如下方式。已经实现了随机模式,我们也可以通过重写类支持其他模式。
    @Configuration
    public class RibbonRuleConfiguration {
    @Bean
    public IRule ribbonRule() {
    return new RandomRule();
    }
    }

    在使用到的control或者service加上使用的ribbon客户端的注解:

    @RibbonClient(name = "center-ribbon-Rule",configuration = RibbonRestTemplateConfiguration.class)
    public class AppApplication {
    
    • step3:使用 RestTemplate远程调用
    restTemplate.getForObject("http://serviceid/restpath", String.class);
    

    Eureka根据 spring.application.name 设置 serviceId,我们只要设置serviceid就可以解析出ip:port。

Feign

Feign 通过注解及拦截器让 Java HTTP 客户端编写更方便。支持可插拔编码器和解码器,降低 HTTP API 的复杂度,通过最少的资源和代码来实现和 HTTP API 的连接。通过可定制的解码器和错误处理,可以编写任意的HTTP API。Spring Cloud Feign 封装了 Ribbon 这一组件,所以在使用 Feign 同时还能提供负载均衡的功能,这一切只需要一个 @FeignClient 即可完成。

  • 配置

    新增依赖 spring-cloud-starter-openfeign

    <dependencies>
        <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    </dependencies>
  • 使用

    1. feign client 注解引入

      在application引入 feign注解

      @EnableFeignClients
      
    2. feign client 接口

      定义rpc服务的接口

      @FeignClient( name = "center-demo", path = "/test", decode404 = true)
      public interface DemoFeignclient {
      @GetMapping("/config")
      String selectconfigByName(@RequestParam String name);
      }
    3. 调用接口

      在service中实例化该接口并使用

          @Autowired
      private DemoFeignclient feignClient;
      
      @RequestMapping("/test/feign")
      public String testconfigrpcByFeign(String name) {
      return "Feign get username <<==>> "+feignClient.selectconfigByName(name);
      }

      我们看到,所有服务提供者供外部调用的接口是一样的,如果每个不同消费者都些feign client 接口,就做了重复的工作,这就是坏味道,所以我们可以像dubbo一样在写服务提供者的时候,就可以把接口单独整个架包出来,供消费者使用。具体可见后面章节的最佳实践。

  • 拦截器

    上面是使用 Feign 来调用简单的远程服务,但实际上远程服务一般会有权限验证(这部分后续章节也会出),需要在 header 中传递 token之类的。在方法中显示传递又过于麻烦了,这时候就可以考虑使用 Feign 提供的RequestInterceptor 接口,只要实现了该接口,那么Feign每次做远程调用之前都可以被它拦截下来在进行包装。示例如下,就可以方便的传递header字段了。

    @Configuration
    public class FeignInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
    requestTemplate.header("token", "ok");
    }
    }
  • 源码

  • PS注意

    Feign与显示声明的Ribbon不要混合在一个controller下使用,不然使用Feign的时候会间歇性提示404错误

          {"timestamp":"2019-03-21T05:37:41.540+0000","status":404,"error":"Not Found","message":"No message available","path":"/test/token/back"}
    

scservers(三)消息总线bus

前言

消息总线bus作为一个轻量化消息中心,通知订阅的方式,完成消息的传递,其基础的应用就是与配置中心结合,通过bus通知client来配置中心拉去更新配置。

消息总线bus

spring cloud 通过封装消息队列rabbitmq,kaffka提供我们bus的一些基础功能。发送配置更新消息。

rabbitmq

安装好rabbitmq服务端,直接使用docker-compose吧,方便。

客户端client

  • 使用bus只需要pom.xml引入如下架包配置
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
  • 同时对rabbitmq 的连接信息进行配置:
spring:
  application:
    name: bus-server
  rabbitmq:
    host: ${RABBIT_MQ_HOST:localhost}
    port: ${RABBIT_MQ_PORT:5672}
    username: ${RABBIT_MQ_USERNAME:guest}
    password: ${RABBIT_MQ_PASSWORD:guest}

即与使用其他mq方式无异。

  • 消息队列注册
@Configuration
public class RabbitMQConfig {
    @Bean
    public Queue testhelloQueue() {
        return new Queue("testhello");
    }

}
  • 消息消费
@Component
@RabbitListener(queues = "testhello")
public class ReceiverDefault {
    private final Logger logger = LoggerFactory.getLogger(ReceiverDefault.class);

    @RabbitHandler
    public void receiver(String hello){
        logger.info("接收消息=====》》》》》{}",hello);
    }
}
  • 消息推送
@Component
public class SenderDefault {
    private final Logger logger = LoggerFactory.getLogger(SenderDefault.class);
   
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sender(String context){
        
        logger.info("发送消息=========》》》》{}",context);
        this.amqpTemplate.convertAndSend("testhello",context);
    }
}

config通知

除了常用的消息队列功能,在微服务的架构中,其与config可以深度结合进行分布式的配置变更的通知。

  • 配置

    配置中心及客户端配置见配置中心一节
  • 流程

    1. 将配置通过git发布
    2. git pull 配置http接口,像配置中心发送通知

      curl -i -X POST http://confighost:port/actuator/bus-refresh
      

      应答:

      HTTP/1.1 204
      Date: Sat, 11 Aug 2018 04:06:35 GMT
      
    3. 源码示例

      center/docker-compose-demo.yaml 文件相关内容

scservers(二) 配置中心

前言

前面介绍了服务注册发现,这个是一个比较核心的功能,还有一个服务治理相关的是配置中心,作为分布式微服务,统一设置更新成千上万的微服务配置,也已经基本成为必选项,即使你的服务没有那么多,但是热更新配置等对生产配置变动的及时更新都有帮助。

配置中心

目前配置中心这一服务比较多,国内外都有比较成熟的经过生产实践的产品,如百度的disconf,携程的apollo,springcloud 也有原生的配置中心config。国内生产用得多的应该是携程的apollo,功能强大。这里就暂时先使用springcoud全家桶,apollo后续再介绍使用。

config



configserver 依托git 完成版本管理,也有本地模式。同时为了及时更新还需要消息总线来支持其及时更新配置。

config使用

  • server

    配置中心可以将服务注册到注册中心,作为服务提供者供其他应用使用。

    1. 使用

      • 新建springboot 工程,添加config starter 傻瓜包:

        <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
      • 在工程启动类添加在工程启动类中,添加注解,启用默认config配置的默认值

        @EnableConfigServer
        
    2. 配置说明

      spring:
      application:
      name: config-server
      cloud:
      config:
      server:
      git:
      uri: https://local/willen/config.git
      searchPaths: /
    3. 示例源码

  • client

  • 5

scservers(一) 注册中心eureka

  • 以前做的项目稳态应用系统一直使用公司研发的一套C++ 的rpc调用框架的esb中间件,加上一些周边插件构成的SOA生态,在此基础上开发业务系统。
  • 后来基于互联网生态的发展壮大,一些创新型的业务我们就使用java开发。并选择了dubbo rpc 框架的作为基础调用。
  • 随后在做一些后台管理应用中接触了springboot 相关内容对java 的配置的简略,约定俗成的规则,真的好用。并想将应用系统框架也升级到springcloud 的生态中去,但是耽搁了下后spring cloud 2都出来了,终于下决心迁移整改。

    # 概要
  • 你们猜对了我们想做微服务,虽然我个人认为soa这样的已经挺好了挺规范,但是趋势还是得推动我们不断学习接受新的技术思想,比如现在都已经出来了servicemesh了,但还是先把微服务搞定吧,虽然我们认为技术细节soa跟微服务差异不大,主要在服务治理上的思想差异吧。
  • 作为这种为应用系统提供框架,提供治理能力的东西,一般需要实现这样的能力:
    1. 技术基础:容器(服务集成),路由,消息通信
    2. 核心功能模块:远程调用模块,服务发现,消息队列,分布式事务
    3. 通用功能模块:对象存储,任务调度等公用的基础工具模块
    4. 服务治理:服务限流降级,分布式配置管理

      # 服务发现

      以前我就说过,原公司自己的rpc的路由,即服务发现基于纯分布式的ospf的路由协议算法+esb总线结合实现,优点是不需要中心统一注册,可以自主发现相邻节点。

      缺点的路由寻址数量有限,需要分中心由一个节点进行截断,即路由网关。同时数据流不能直接发送到目标节点,而需要层层转发(只与相邻节点建立连接),虽然会增加不必要的网络压力,却也可以给分中心的网络隔离提供机会(路由网关可以起到跳板机作用)。这种私有协议基础上的功能在特定场景合适,但是不具有广泛性,社区活跃度,eureka这种基于rest接口的c/s结构的服务注册发现服务:便于管理,易用性,适用性强。

eureka

如上图我们可以看到服务提供方注册到eureka-server上,然后服务消费方通过eureka-client从eureka获取到可用服务,然后就可以进行直接的远程调用了。这就实现了对rpc的路由能力的治理。
如果服务较多,eureka server面临的性能压力也比较大,延时等,但是最主要的还是需要高可用能够提供持续不宕机服务。

eureka使用

  • server

    springcloud基于springboot,其开箱即用的原则让我们不需要过多配置就可以傻瓜式的使用eureka,后续源码解析留待下次。

    这里就简单列出配置与说明:

    1. 使用

      • 新建springboot工程,添加eureka starter 傻瓜架包:
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
      </dependency>
      • 启动类添加注解,表明事eurekaServer应用,并启用eurekaServer默认配置:
      @EnableEurekaServer // 启用 eureka server 相关默认配置 
      
    2. 简易配置说明

      server:
      port: ${EUREKA_PORT:8761}
      eureka:
      # 生产环境中官方是不建议修改默认配置,因为那样会破坏 eureka server 的保护模式
      server:
      # 关闭保护模式(生产环境不建议修改)
      enable-self-preservation: false
      # 清理间隔(默认是60 * 1000 毫秒)(生产环境不建议修改)
      eviction-interval-timer-in-ms: 60000
      # Eureka 拉取服务列表时间(默认:30秒)(生产环境不建议修改)
      remote-region-registry-fetch-interval: 5
      client:
      # eureka server 没必要自己把自己注册上去,所以可以设置成 false
      registerWithEureka: false
      # 是否从Eureka Server上获取注册信息,默认为true,此处建议修改成 false (单机设置的意义不大,如果设置成 true 启动会去抓取一次注册表,获取不到更新缓存就会出错(该错误不影响 eureka 正常使用))
      fetchRegistry: false
      serviceUrl:
      # 默认注册地址 this.serviceUrl.put("defaultZone", "http://localhost:8761/eureka/");
      # 划重点:此处的 defaultZone 千万别写成 default-zone
      defaultZone: http://${EUREKA_ZONE_HOST:localhost}:${EUREKA_ZONE_PORT:8761}/eureka/
      spring:
      application:
      name: eureka-server
    3. 示例源码

  • client

apigateway——Kong介绍(一)

网关

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

作用

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

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

image——>image

对接不同客户:

image

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

Kong

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

image

image

安装

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

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

1.新建docker-compose.yml:内容下
version: &#39;2.2&#39;

services:

  kong-database:
    image: postgres:9.4-alpine
    container_name: kong-database
    environment:
      - POSTGRES_USER=kong
      - POSTGRES_DB=kong
    healthcheck:
      test: &quot;pg_isready -U kong &amp;&amp; psql -d kong -U kong -c \&quot;SELECT 1=1\&quot;&quot;
      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 &quot;kong migrations up &amp;&amp; touch migrations_run &amp;&amp; sleep 30&quot;
    healthcheck:
      test: &quot;if [[ -f migrations_run ]] ; then exit 0; else exit 1; fi&quot;
      interval: 10s
      timeout: 5s
      retries: 5

  kong:
    image: kong:${KONG_VERSION}
    container_name: kong
    depends_on:
      kong-migration:
        condition: service_healthy
    healthcheck:
      test: &quot;kong health&quot;
      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

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

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

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));  
                      	    }  
                      }

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