SpingCloudDemo

226 阅读7分钟

前言

   springcloud是微服务架构的集大成者,将一系列优秀的组件进行了整合。基于springboot构建,多用于大型互联网架构,笔者今天就带大家一步步搭建springcloud框架,本框架使用idea的多模块架构集成了注册中心eureka,网关zuul,缓存redis,熔断hystrix,负载均衡ribbon,源码已上传至github,有需要的童鞋可以下载 

准备工作

   本demo涉及到的工具:idea2016,mysql5.6,redis(windows)

   环境:jdk1.8

   需要对springboot,rpc原理有一定的了解

   源码地址:github.com/qw870602/my…

正文

   项目结构截图


模块说明


演示流程

1.用户登陆,后台生成一个令牌存入redis,并将令牌下发

2.用户拿令牌查询用户信息,顺序为postman--zuul--sendCodePlatformUserService--mysql

3.用户保存订单信息,顺序为postman--zuul--sendCodePlatformQrService--sendCodePlatformUserService

工程的pom.xml,其中的commonApi不是一个服务,而是作为一个模块被打成jar包给其它服务使用,可以类比为我们平时项目中引入的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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.lovnx</groupId>
    <artifactId>trafficQrService</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>eureka-sever</module>
        <module>zuul</module>
        <module>commonApi</module>
        <module>sendCodePlatformUserService</module>
        <module>sendCodePlatformQrService</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
            <version>1.3.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency>
    </dependencies>
</project>

先看一下注册中心的 application.yml

eureka:
  client:
    register-with-eureka: false #服务注册中心也会将自己作为客户端来尝试注册自己,为true(默认)时自动生效
    fetch-registry: false  #检索服务选项,当设置为True(默认值)时,会进行服务检索,注册中心不负责检索服务
    serviceUrl:
      defaultZone: http://localhost:${server.port}/eureka/
  server:
    #测试环境关闭自我保护机制,正式环境需要打开,因为有可能心跳通讯阻塞,服务是正常的
    enable-self-preservation: false
    #清理无效节点,默认60*1000毫秒,即60秒,扫描失效服务的间隔时间
    eviction-interval-timer-in-ms: 5000

server:
  port: 7070

spring:
  application:
    name: eureka-server

启动注册中心,登陆http://localhost:7070/, 我们发现还没有服务注册到Eureka中

看一下数据库的表结构

用户表


订单表


我们看一下用户服务项目的application.yml

eureka:
  client:
    serviceUrl:
      #正式环境Eureka做主副备份,需要2个地址
      defaultZone: http://localhost:7070/eureka/
  instance:
    #eureka客户端需要多长时间发送心跳给eureka服务器,表明他仍然活着,默认30秒
    lease-renewal-interval-in-seconds: 5
    #eureka服务器在接受到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认90S
    #如果该值太大,则很可能将流量转发过去的时候,该instance已经不存活了。如果设置太小了,则instance则很可能因为临时的网络抖动而被摘除掉
    lease-expiration-duration-in-seconds: 10

server:
  port: 7074

spring:
  application:
    name: sendCodePlatformUserService
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  datasource:
      druid:
        #jdbc配置
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true
        username: root
        password: 123456
        #连接池配置
        initial-size: 5
        min-idle: 5
        max-active: 10
        max-wait: 50000

mybatis:
  mapper-locations: classpath:mapping/*.xml

management:
  security:
    enabled: false

需要告诉该服务注册中心的地址,指定该服务的name,即在注册中心的serviceId,数据库的相关信息,启动数据库(用到的表和数据可根据项目人造)和该服务,登陆注册中心


说明用户服务已经注册到Eureka中了

再看一下订单服务的application.yml

eureka:
  client:
    serviceUrl:
      #正式环境Eureka做主副备份,故这里预留2个地址
      defaultZone: http://localhost:7070/eureka/
  instance:
    #eureka客户端需要多长时间发送心跳给eureka服务器,表明他仍然活着,默认30秒
    lease-renewal-interval-in-seconds: 5
    #eureka服务器在接受到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认90S
    #如果该值太大,则很可能将流量转发过去的时候,该instance已经不存活了。如果设置太小了,则instance则很可能因为临时的网络抖动而被摘除掉
    lease-expiration-duration-in-seconds: 10

server:
  port: 7075

spring:
  application:
    name: sendCodePlatformQrService
  jackson:
      date-format: yyyy-MM-dd HH:mm:ss
      time-zone: GMT+8
  datasource:
      druid:
        #jdbc配置
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true
        username: root
        password: 123456
        #连接池配置
        initial-size: 5
        min-idle: 5
        max-active: 10
        max-wait: 50000

mybatis:
    mapper-locations: classpath:mapping/*.xml

management:
    security:
      enabled: false

#设置hystrix超时时间为40S,默认1S服务无响应就直接返回了
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 50000

ribbon:
  ReadTimeout: 50000
  ConnectTimeout: 50000

启动服务,并登陆注册中心


我们发现订单服务也注册进去了

再看一下网关zuul的application.yml

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:7070/eureka/
    #表示eureka client间隔多久去拉取服务器注册信息,默认为30秒
    registry-fetch-interval-seconds: 5

server:
  port: 7072

spring:
  application:
    name: zuul
  ## Redis 配置
  ## Redis数据库索引(默认为0)
  redis:
    database: 0
    ## Redis服务器地址
    host: 127.0.0.1
    ## Redis服务器连接端口
    port: 6379
    #Redis服务器连接密码(默认为空)
    ## 连接池最大连接数(使用负值表示没有限制)
    pool:
      max-active: 8
      ## 连接池最大阻塞等待时间(使用负值表示没有限制)
      max-wait: -1
      ## 连接池中的最大空闲连接
      max-idle: 8
      ## 连接池中的最小空闲连接
      min-idle: 0
      ## 连接超时时间(毫秒)
      timeout: 30000

zuul:
  routes:
    #用户类请求
    api-a:
      path: /api/user/**
      serviceId: sendCodePlatformUserService
    #发码类请求
    api-b:
      path: /api/qr/**
      serviceId: sendCodePlatformQrService
  #host:
      #url配置生效
      socket-timeout-millis: 50000
      connect-timeout-millis: 50000

#设置hystrix超时时间为50S,默认1S服务无响应就直接返回了
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 20000

#ribbon:
  # 请求处理的超时时间
  ReadTimeout: 50000
  #请求连接的超时时间
  ConnectTimeout: 50000

启动zuul并登陆注册中心


我们发现zuul也注册进去了

redis主要用来缓存用户的token信息,避免将用户的token存入数据库,提高并发能力,网关配置那里通过api-a,api-b进行路由分发,serviceId是服务中配置的name

启动本地的redis

调用postman


因为token无效,所以查询失败,需要先登陆拿到token

调用登陆


登陆成功,拿到token

调用用户信息查询


查询成功

接下来我们新增一笔订单,请求会通过网关先到订单服务,订单服务调用用户服务的查询接口以核实用户的有效性,然后新增一笔订单

订单服务相关代码,通过feign调用用户服务

public class QrController {

   private final Logger logger = Logger.getLogger(getClass());
   @Autowired
   private UserFeignClient userFeignClient;
   @Autowired
   private QrTicketInfoFacade qrTicketInfoFacade;

   @RequestMapping(value = "/saveQrOrder" ,method = RequestMethod.POST)
   public BasePojoObj saveQrOrder(@RequestBody ReqApiParam clientParam) {
      BasePojoObj result = new BasePojoObj();
      try {
         //Thread.sleep(40000);
         //通过feign调用用户相关服务
         BasePojoObj obj=userFeignClient.getUserInfo(clientParam);
         if(obj!=null) {
            qrTicketInfoFacade.saveQrTicketInfos();
            result.setStatus(BasePojo.CODE_SUCCESS);
            result.setMsg("新增订单成功");
         }
      } catch (Exception e) {
         logger.error("异常:{}",e);
         result.setStatus(BasePojo.CODE_FAIL);
         result.setMsg(e.getMessage());
      }
      return result;
   }
}
@FeignClient("sendCodePlatformUserService")
public interface UserFeignClient {

   @RequestMapping(value = "/getUserInfo" ,method = RequestMethod.POST)
   BasePojoObj getUserInfo(@RequestBody ReqApiParam clientParam);
}

请求新增一笔订单


我们发现订单服务调用用户服务查询接口成功了,并且新增了一笔订单

总结

    我们看到springcloud非常灵活,能够把业务进行拆分,比较适合大型项目,除了这些组件,springcloud还有配置中心Config, Bus消息总线,Sleuth分布式服务追踪等等,每种组件必然有其存在的意义,一般是为了解决某一类问题,所以springcloud的博大精深值得大家研究一番。