SpringCloud工程入门

项目结构:

首先创建一个Maven父工程:springcloud-example,相关的pom.xml如下:

<?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.cn</groupId>
    <artifactId>springcloud-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/>
    </parent>
    <modules>
        <module>user-service</module>
        <module>consumer-demo</module>
        <module>eureka-server</module>
        <module>zuul-demo</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR2</spring-cloud.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>2.0.4</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.32</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
View Code

 接着创建服务提供方:user-service,pom.xml如下:

<?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">
    <parent>
        <artifactId>springcloud-example</artifactId>
        <groupId>com.cn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>user-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
        </dependency>
        <!-- 除了eureka服务方,其他引入的都是客户端client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

</project>
View Code

对应的启动方法UserApplication

package com.cn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
 * Created by 13632 on 2018-12-11.
 */
@SpringBootApplication
@tk.mybatis.spring.annotation.MapperScan("com.cn.mapper")
//启动Eureka注册中心
@EnableDiscoveryClient
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}
View Code

pojo类

通用mapper需要加上@Table、@Id、@KeySql等注解

package com.cn.pojo;
import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;

import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;

/**
 * Created by 13632 on 2018-12-11.
 */
//因为引入了lombok插件,所有只要使用@Data注解就能自动实现get、set、toString等方法
@Data
//连接数据库的user表
@Table(name = "user")
public class User {
    //主键自增
    @Id
    @KeySql(useGeneratedKeys = true)
    private Integer id;
    private String username;
    private String password;
    private Date birth;
    private String gender;
    private String email;
    private Integer status;
    private Date regtime;
}
View Code

mapper类

package com.cn.mapper;

import com.cn.pojo.User;
import tk.mybatis.mapper.common.Mapper;

/**
 * Created by 13632 on 2018-12-11.
 */
//继承tk.mybatis的Mapper类就可以不用在写相关的mapper方法
public interface UserMapper extends Mapper<User> {
}
View Code

service

package com.cn.service;

import com.cn.mapper.UserMapper;
import com.cn.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by 13632 on 2018-12-11.
 */
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public User queryById(Integer id) {

        return userMapper.selectByPrimaryKey(id);
    }
}
View Code

web

package com.cn.web;

import com.cn.pojo.User;
import com.cn.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by 13632 on 2018-12-11.
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Integer id) {
        //这个代码测试用,让它休眠2秒,实现服务熔断,可以删掉
//        try {
//            Thread.sleep(2000L);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        return userService.queryById(id);
    }
}
View Code

application.yml文件

server:
  port: 8180
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/ssm_blog
    username: root
    password: 85586537
  application:
    name: user-service
mybatis:
  type-aliases-package: com.cn.pojo
#注册到eureka
eureka:
  client:
    service-url:
    #高可用
     defaultZone: http://server1:10086/eureka,http://server2:10087/eureka
    #服务提供方需要配置下面这些,默认采用官方的值就可以,下面这些数字就是默认
#  instance:
  #每隔30秒向Eureka发一次心跳,那么服务还活着
    #lease-renewal-interval-in-seconds: 30
  #如果超过90s没有发心跳的话,表示Eureka挂了,那么Eureka的状态会显示成DOWN
    #lease-expiration-duration-in-seconds: 90
View Code

    服务的调用方(消费方):consumer-demo

相关的pom.xml文件

<?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">
    <parent>
        <artifactId>springcloud-example</artifactId>
        <groupId>com.cn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>consumer-demo</artifactId>
    <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>
        <!-- ribbon负载均衡,如果使用了feign,这个依赖可以不用引入-->
        <!--<dependency>-->
            <!--<groupId>org.springframework.cloud</groupId>-->
            <!--<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>-->
        <!--</dependency>-->
        <!-- hystri熔断机制-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--声明式服务调用feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

</project>
View Code

启动类:ConsumerApplication

package com.cn;

import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * Created by 13632 on 2018-12-11.
 */
/*SpringCloudApplication包含了@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker
 */
@SpringCloudApplication
@EnableFeignClients  //开启Feign
public class ConsumerApplication {
    //有了feign,负载均衡可以省略了,因为默认加了负载均衡
//    @Bean
//    //负载均衡
//    @LoadBalanced
//    public RestTemplate restTemplate() {
//        return new RestTemplate();
//    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}
View Code

pojo类

package com.cn.pojo;

import lombok.Data;
import java.util.Date;
/**
 * Created by 13632 on 2018-12-11.
 * 调用方直接调用提供方就可以,所以数据库那些不需要了
 */
@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private Date birth;
    private String gender;
    private String email;
    private Integer status;
    private Date regtime;
}
View Code

web层,因为使用了Feign远程调用,实现接口进行调用,使代码看起来专业点。

package com.cn.web;

import com.cn.client.UserClient;
import com.cn.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by 13632 on 2018-12-11.
 */
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
    @Autowired
    private UserClient userClient;
    @Autowired
    private DiscoveryClient discoveryClient;

    //feign案例
    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Integer id) {
        //因为引入了ribbon负载均衡,因此只需要http://服务的id/即可。
        return userClient.queryById(id);
    }
}
View Code

接口

package com.cn.client;

import com.cn.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
 * Created by 13632 on 2018-12-17.
 * Feign远程服务调用接口
 * 需要请求方法、请求路径、请求参数、返回参数这4个值
 * 如果要实现服务熔断的话,需要在@FeignClient加上实现方法类
 */
//获取服务列表
@FeignClient(value = "user-service",fallback = UserClientFallBack.class)
public interface UserClient {
    @GetMapping("user/{id}")
    User queryById(@PathVariable("id") Integer id);
}
View Code

接口实现类

package com.cn.client;

import com.cn.pojo.User;
import org.springframework.stereotype.Component;
/**
 * Created by 13632 on 2018-12-17.
 * feign的熔断需要自己配置,实现对应的接口,然后写上熔断逻辑方法
 */
//交给spring管理
@Component
public class UserClientFallBack implements UserClient {
    @Override
    public User queryById(Integer id) {
        User user = new User();
        user.setUsername("未知用户");
        return user;
    }
}
View Code

application.yml配置

server:
  port: 8280
spring:
  application:
    name: consumer-service
#注册到eureka
eureka:
  client:
    service-url:
     defaultZone: http://server1:10086/eureka,http://server2:10087/eureka
      #服务消费方,fetch-registry是否要拉取服务列表,默认为true, 下面的这个配置是每隔X秒拉取一次。
    #fetch-registry: true
    #registry-fetch-interval-seconds: 30
#feign开启熔断
feign:
  hystrix:
    enabled: true
#feign的负载均衡,ribbon的超时时长默认的话(read+connect)*2小于hystrix的超时时长
ribbon:
  ConnectionTimeout: 500
  ReadTimeout: 2000
#针对全局超时连接配置,可以直接采用默认1秒,
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
View Code

    注册中心:Eureka-server

Pom.xml文件

<?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">
    <parent>
        <artifactId>springcloud-example</artifactId>
        <groupId>com.cn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>eureka-server</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

</project>
View Code

启动类EurekaServerApplication

package com.cn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * Created by 13632 on 2018-12-12.
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
View Code

  application.yml配置具体参考前一篇高可用配置

   网关:zuul-demo

pom.xml文件

<?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">
    <parent>
        <artifactId>springcloud-example</artifactId>
        <groupId>com.cn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>zuul-demo</artifactId>
    <dependencies>
        <!--zuul网关 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 这个是因为里面StringUtils方法,所以才引入的,如果不需要可以删除-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

</project>
View Code

启动类:ZuulApplication

package com.cn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
/**
 * Created by 13632 on 2018-12-17.
 */
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }
}
View Code

Zuul的作用就是进行拦截,判断是否有权限

package com.cn.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
 * Created by 13632 on 2018-12-18.
 * 判断用户是否有access-token,如果没有就拦截,有就放行
 */
@Component
public class LoginFilter extends ZuulFilter {
    /*
    拦截器类型:包括pre、route、post、error
    pre:请求之前进行拦截,常用这个
    route:请求到达后拦截
    error:抛出异常拦截
    post:在route和error之后执行
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
    //请求顺序,数据越小优先级越高
    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
    }
    //是否拦截
    @Override
    public boolean shouldFilter() {
        return true;
    }
    //
    @Override
    public Object run() throws ZuulException {
        //这步的话直接打开ZuulFilter的继承关系中的FormBodyWrapperFilter的run方法即可看到
        //获取请求上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        //获取request
        HttpServletRequest request = ctx.getRequest();
        //获取请求参数access-token
        String token = request.getParameter("access-token");
        //判断是否存在
        if (StringUtils.isBlank(token)) {
            /*
            不存在,未登录,进行拦截
            因为是用zuul实现的,所以需要下面这个方法
            然后返回一个状态码:403 FORBIDDEN 表示禁用
             */
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
        }
        return null;
    }
}
View Code

application.yml

server:
  port: 10010
#zuul需要到eureka拉取服务列表,此外,它默认实现了负载均衡,所有需要进行配置
eureka:
  client:
    service-url:
     defaultZone: http://server1:10086/eureka,http://server2:10087/eureka
spring:
  application:
    name: zuul-service
ribbon:
  ConnectionTimeout: 500
  ReadTimeout: 1000
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 4000
#下面这个配置是可以设置不能某个服务不能用网关访问,是一个集合,注意配置格式
#zuul:
#  ignored-services:
#    - consumer-service
#    - user-service

#下面这个配置可以直接忽略,因为默认的话直接访问http://ip+端口/服务Id/路径,比如可以直接访问
#localhost:10010/user-service/user/2等
#zuul:
#  routes:
#    user-service: /user-service/**
View Code

 

posted @ 2018-12-18 23:02  曾饺  阅读(375)  评论(0)    收藏  举报