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>
接着创建服务提供方: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>
对应的启动方法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); } }
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; }
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> { }
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); } }
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); } }
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
服务的调用方(消费方):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>
启动类: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); } }
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; }
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); } }
接口
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); }
接口实现类
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; } }
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
注册中心: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>
启动类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); } }
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>
启动类: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); } }
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; } }
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/**

浙公网安备 33010602011771号