1、SpringCloud项目搭建

整个项目采用了Gradle工具进行构建,首先是构建一个纯净的Gradle项目,一个空的项目,为整个SpringCloud项目提供一个大的整合范围,
同时本项目采用lombok插件实现日志打印,所以IDEA需要安装Lombok的插件,eclipse同理,个人推荐使用IDEA

jeecg关闭springCloudBus_maven

首先最外层的gradle项目的build.gradle文件撰写,引入基础的jar包,和基础环境的配置,整个大项目的构建层次如上图

buildscript {
    // 定义变量
    ext {
        springBootVersion = '2.0.0.RELEASE'
        ALI_REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content/groups/public'
    }
    // 仓库地址
    repositories {
        // 先从阿里云maven仓库中去下载,没有再去maven中央仓库
        maven {
            url ALI_REPOSITORY_URL
        }
        mavenCentral()

    }
    // 构建时所需要的插件或者是其他的依赖
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }

}
// 所有模块的总仓库地址
allprojects {
    // 在build.gradle文件中直接声明的依赖项、仓库地址等信息是项目自身需要的资源。
    repositories {
        maven {
            url ALI_REPOSITORY_URL
        }
    }
}
subprojects {
    apply plugin: 'java'
    apply plugin: 'war';
    apply plugin: 'idea'
    apply plugin: 'io.spring.dependency-management'
    apply plugin: 'org.springframework.boot'
    sourceCompatibility = 1.8
    targetCompatibility = 1.8
    // 指定编码格式
    [compileJava,compileTestJava,javadoc]*.options*.encoding = 'UTF-8'
    version = "1.0.0-SNAPSHOT"  // 开发版本: -SNAPSHOT 正式版本:.RELEASE
    description = "spring boot in action"
    dependencies {
        // SpringBoot测试类需要的jar包
        testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test'
        //lombok的jar包
        compileOnly 'org.projectlombok:lombok:1.18.10'
        annotationProcessor 'org.projectlombok:lombok:1.18.10'
        testCompileOnly 'org.projectlombok:lombok:1.18.10'
        testAnnotationProcessor 'org.projectlombok:lombok:1.18.10'
        //swagger驱动
        compile 'io.springfox:springfox-swagger-ui:2.9.2'
        compile 'io.springfox:springfox-swagger2:2.9.2'
        compile 'io.github.swagger2markup:swagger2markup:1.3.1'
        compile 'joda-time:joda-time:2.9.4'

    }
}

 

1.1、编写Common模块

common主要目的 是将所有项目均需要使用的jar包和工具类的整合,例如DateUtil,统一返回类等,因为common不会单独成为一个项目,所以不需要配置yml

Common模块的build.gradle文件书写

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    //SpringBootWeb模块
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
    //springboot的aop切面驱动
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-aop'

    compile 'org.hibernate:hibernate-validator:5.3.6.Final'
    compile 'com.fasterxml.jackson.core:jackson-databind:2.9.7'
}
jar.enabled=true

1.2、编写Eureka模块

Eureka模块的build.gradle文件书写

dependencies {
    //首先引入common模块
    compile project(":oa-common")
    testCompile group: 'junit', name: 'junit', version: '4.12'
    //Eurake注册中心依赖
    compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-eureka-server', version: '2.0.0.RELEASE'
    // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies
    runtime group: 'org.springframework.cloud', name: 'spring-cloud-dependencies', version: 'Finchley.SR4'
}

此时编写启动类和对应的yml文件,因为个人习惯将不同的属性分别写在application.ymlbootstrap.yml两个文件中

1.2.1、编写bootstrap.yml

# 应用名称
spring:
  application:
    name: eureka-server
server:
  port: 9001 #服务端口

1.2.2、编写application.yml文件

eureka:
  instance:
    # 使用 ip 代替实例名
    prefer-ip-address: true
    # 实例的主机名
    hostname: ${spring.cloud.client.ip-address}
    # 实例的 ID 规则
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
#    hostname: localhost
  client:
    #是否将eureka自身作为应用注册到eureka注册中心
    register-with-eureka: false
    #为true时,可以启动,但报异常:Cannot execute request on any known server
    fetch-registry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/

#基本信息
info:
  description: Eureka注册中心
  version: v1.0
  author: Eureka

1.2.3、编写Eureka项目的启动类

package com.js;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
 * @Description Eurake注册中心,启动时看佛头就行了,不用看那些小字,眼睛挺痛的
 * @Author Eureka
 */
@Slf4j
@EnableEurekaServer
@SpringBootApplication
public class OaEurakeStart {
    public static void main(String[] args) {
        try{
            SpringApplication.run(OaEurakeStart.class,args);
            log.info(print());
            log.info("项目启动成功");
        }catch (Exception e){
            log.info("项目启动失败");
        }
    }
    private static synchronized String print() {
        StringBuilder sb = new StringBuilder();
        sb.append("\n");
        sb.append("                   _ooOoo_\n");
        sb.append("                  o8888888o\n");
        sb.append("                  88\" . \"88\n");
        sb.append("                  (| -_- |)\n");
        sb.append("                  O\\  =  /O\n");
        sb.append("               ____/`---'\\____\n");
        sb.append("             .'  \\\\|     |//  `.\n");
        sb.append("            /  \\\\|||  :  |||//  \\ \n");
        sb.append("           /  _||||| -:- |||||-  \\ \n");
        sb.append("           |   | \\\\\\  -  /// |   |\n");
        sb.append("           | \\_|  ''\\---/''  |   |\n");
        sb.append("           \\  .-\\__  `-`  ___/-. /\n");
        sb.append("         ___`. .'  /--.--\\  `. . __\n");
        sb.append("      .\"\" '<  `.___\\_<|>_/___.'  >'\"\".\n");
        sb.append("     | | :  `- \\`.;`\\ _ /`;.`/ - ` : | |\n");
        sb.append("     \\  \\ `-.   \\_ __\\ /__ _/   .-` /  /\n");
        sb.append("======`-.____`-.___\\_____/___.-`____.-'======\n");
        sb.append("                   `=---='\n");
        sb.append("...................................................\n");
        return sb.toString();
    }
}

1.3、编写应用服务模块

因为正常来说,SpringCloud本身存在生产者和消费者,为了演示方便我就只写一个生产者进行测试,消费者通过PostMan的请求模拟.

该模块的build.gradle文件书写(图片中的api模块是为SpringCloudfeignClient准备的)

dependencies {
    compile project(":oa-common")
    testCompile group: 'junit', name: 'junit', version: '4.12'
    //Eurake注册中心依赖
    compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-eureka-server', version: '2.0.0.RELEASE'
    // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies
    runtime group: 'org.springframework.cloud', name: 'spring-cloud-dependencies', version: 'Finchley.SR4'
    // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign
    compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '2.0.0.RELEASE'
}
description = "oa运营模块服务"

 

1.3.1、编写bootstrap.yml

# 应用名称
spring:
  application:
    name: oa-operation-service
server:
  port: 8080 #服务端口

1.3.2、编写application.yml文件

server:
  tomcat:
    # 该线程池可以容纳的最大线程数。默认值:200;
    maxThreads: 300
    # 最大连接线程数,即:并发处理的最大请求数,默认值为 75
    maxConnections: 3000
    # 允许的最大连接数,应大于等于 maxProcessors ,默认值为 100
    acceptCount: 1000
    # 编码方式
    uri-encoding: UTF-8
spring:
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
application:
  base-package: com.js

#基本信息
info:
  description: oa-operation-service
  version: v1.0
  author: operation
eureka:
  instance:
    # 使用 ip 代替实例名
    prefer-ip-address: true
    # 实例的主机名
    hostname: ${spring.cloud.client.ip-address}
    # 实例的 ID 规则
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
  client:
    serviceUrl:
      defaultZone: http://localhost:9001/eureka/
    #    encoder-name:
    #    register-with-eureka: false
    fetch-registry: false
  application:
    name: oa-operation-service

1.3.3、编写oa-operation-service的启动类

package com.js;

import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @Description 启动类
 */
@EnableEurekaClient
@SpringBootApplication
@Slf4j
public class OaServiceStart {
    public static void main(String[] args) {
        try{
            SpringApplication.run(OaServiceStart.class,args);
            log.info(print());
            log.info("项目启动成功");
        }catch (Exception e){
            log.info("项目启动失败");
        }
    }
     private static synchronized String print() {
        StringBuilder sb = new StringBuilder();
        sb.append("\n");
        sb.append("                   _ooOoo_\n");
        sb.append("                  o8888888o\n");
        sb.append("                  88\" . \"88\n");
        sb.append("                  (| -_- |)\n");
        sb.append("                  O\\  =  /O\n");
        sb.append("               ____/`---'\\____\n");
        sb.append("             .'  \\\\|     |//  `.\n");
        sb.append("            /  \\\\|||  :  |||//  \\ \n");
        sb.append("           /  _||||| -:- |||||-  \\ \n");
        sb.append("           |   | \\\\\\  -  /// |   |\n");
        sb.append("           | \\_|  ''\\---/''  |   |\n");
        sb.append("           \\  .-\\__  `-`  ___/-. /\n");
        sb.append("         ___`. .'  /--.--\\  `. . __\n");
        sb.append("      .\"\" '<  `.___\\_<|>_/___.'  >'\"\".\n");
        sb.append("     | | :  `- \\`.;`\\ _ /`;.`/ - ` : | |\n");
        sb.append("     \\  \\ `-.   \\_ __\\ /__ _/   .-` /  /\n");
        sb.append("======`-.____`-.___\\_____/___.-`____.-'======\n");
        sb.append("                   `=---='\n");
        sb.append("...................................................\n");
        return sb.toString();
    }
}

因为想要模拟生产者,我们写一个简单的Controller

package com.js.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description Swagger测试controller
 */
@RestController
@Api("测试Controller")
public class HelloController {
    private String url = "HelloController";

    @GetMapping("test")
    @ApiOperation(value = "测试Controller", notes = "测试Controller")
    public String testSwagger() {
        System.out.println("测试分支");
        return url;
    }
}

此时我们访问http://localhost:9001就可以看到项目是否注册到注册中心

1.4、编写oa-gateway模块

因为正常来说,SpringCloud本身存在生产者和消费者,为了演示方便我就只写一个生产者进行测试,消费者通过PostMan的请求模拟.

该模块的build.gradle文件书写(图片中的api模块是为SpringCloudfeignClient准备的),在SpringBoot2.x版本存在两种路由转发方法,一种是zuul组件,另一种是gateway,本次采用zuul,当采用Spring web启动时无法通过gateway进行路由转发

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'

    compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:2.0.0.RELEASE'

    compile 'org.springframework.cloud:spring-cloud-starter-netflix-zuul:2.0.0.RELEASE'
}

 

1.4.1、编写bootstrap.yml

# 应用名称
spring:
  application:
    name: oa-gateway
server:
  port: 10000 #服务端口

1.4.2、编写application.yml文件

eureka:
  instance:
    # 使用 ip 代替实例名
    prefer-ip-address: true
    # 实例的主机名
    hostname: ${spring.cloud.client.ip-address}
    # 实例的 ID 规则
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
  client:
    serviceUrl:
      # 注册中心地址
      defaultZone: http://${eureka.instance.hostname}:9001/eureka/
logging:
  level:
    # log 级别
    org.springframework.cloud.gateway: debug
#自定义路由映射
zuul:
  routes:
    oa-operation-service: /operation/**
    oa-user-service: /user/**
  #统一入口为上面的配置,其他入口忽略
  ignored-patterns: /*-service/**
  host:
    connect-timeout-millis: 3000
    socket-timeout-millis: 3000
  #忽略整个服务,对外提供接口
#  ignored-services: oa-service
ribbon:
  ReadTimeout: 3000
  ConnectTimeout: 3000
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeout-in-milliseconds: 3000

1.3.3、编写oa-gateway的启动类

package com.js;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
 * @Description 网关启动类
 * @EnableZuulProxy 网关注解
 */
@Slf4j
@EnableDiscoveryClient
@SpringBootApplication
@EnableZuulProxy
public class OaGateWayStart {
    public static void main(String[] args) {
        try{
            SpringApplication.run(OaGateWayStart.class,args);
            log.info(print());
            log.info("项目启动成功");
        }catch (Exception e){
            log.info("项目启动失败");
        }
    }
    private static synchronized String print() {
        StringBuilder sb = new StringBuilder();
        sb.append("\n");
        sb.append("                   _ooOoo_\n");
        sb.append("                  o8888888o\n");
        sb.append("                  88\" . \"88\n");
        sb.append("                  (| -_- |)\n");
        sb.append("                  O\\  =  /O\n");
        sb.append("               ____/`---'\\____\n");
        sb.append("             .'  \\\\|     |//  `.\n");
        sb.append("            /  \\\\|||  :  |||//  \\ \n");
        sb.append("           /  _||||| -:- |||||-  \\ \n");
        sb.append("           |   | \\\\\\  -  /// |   |\n");
        sb.append("           | \\_|  ''\\---/''  |   |\n");
        sb.append("           \\  .-\\__  `-`  ___/-. /\n");
        sb.append("         ___`. .'  /--.--\\  `. . __\n");
        sb.append("      .\"\" '<  `.___\\_<|>_/___.'  >'\"\".\n");
        sb.append("     | | :  `- \\`.;`\\ _ /`;.`/ - ` : | |\n");
        sb.append("     \\  \\ `-.   \\_ __\\ /__ _/   .-` /  /\n");
        sb.append("======`-.____`-.___\\_____/___.-`____.-'======\n");
        sb.append("                   `=---='\n");
        sb.append("...................................................\n");
        return sb.toString();
    }
}

此时,我们访问Eurekahttp://localhost:9001会看到网关服务和生产服务都已经成功注册

我们对生产者项目直接进行访问http://localhost:8080/test,数据正常返回,因为我所返回的参数进行自定义类的包装可能显示有区别

而我们配置网关的目的是不直接访问这个项目此时我们测试访问网关路径http://localhost:10000/operation/test会发现展示相同的效果,也就是说网关拦截到我们配置的带有/operation/**的路径之后,转发到了oa-operation-service对应的服务,这是在有一台服务的情况下,在微服务架构下,任何一台服务不会只有单独一个项目在运行,此时网关会随机发放到其中一台服务上,也就是轮询机制。

1.5、正式消费者调用生产者

存在两种形式调用,一种是http请求,而另一种就是feignClient的形式调用,主要说明feignClient方法

oa-operation-service,需要释放api时,我们首先将oa-operation-api模块引入oa-operation-service

我们需要在oa-operation-servicebuild.gradle下引入

compile project(":oa-operation-api")

1.5.1、定义接口

package com.js.api.system;

import com.js.form.BasePageForm;
import com.js.form.SysLogForm;
import com.js.response.BaseResponse;
import com.js.vo.PageResVo;
import com.js.vo.system.SysLogVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;

@RequestMapping("/system/log")
@Api("系统日志Controller")
public interface LogControllerApi {

    @PostMapping("getLog")
    @ApiOperation(value = "分页获取系统日志", notes = "分页获取系统日志")
    BaseResponse<PageResVo<SysLogVo>> getLogMess(@RequestBody BasePageForm basePageForm);

    @GetMapping("addLog")
    @ApiOperation(value = "添加系统日志", notes = "添加系统日志")
    void addLog(@RequestBody SysLogForm sysLogForm);
}

当调用者和生产者不再同一个项目下时,jar包的传递就比较依赖私服的存在,如果是像我的项目一样,oa-user-service想要调用oa-operation-service接口时,可以在oa-user-service直接引入oa-operation-api

此时我们需要创建一个feignClient的配置

package com.js.config;

import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;

/**
 * @Description: 扫描feignClient并加载
 * @Date: 2018/10/27
 */
@Configuration
@EnableFeignClients(basePackages = {"com.js.feignclient"})
public class FeignClientConfiguration {
    
}

接下来我们需要些一个代理服务

package com.js.feignclient;

import com.js.api.system.LogControllerApi;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * @Description:value值为想要引入项目的服务名此时你会发现注解Autowired注入LogProxyClient
 * 到某个方法里时可以通过.的形式调用
 */
@FeignClient(value = "oa-operation-service")
public interface LogProxyClient extends LogControllerApi {

}