项目结构:
首先创建一个Maven父工程:springcloud-example,相关的pom.xml如下:
4.0.0 com.cn springcloud-example 1.0-SNAPSHOT pom org.springframework.boot spring-boot-starter-parent 2.0.3.RELEASE user-service consumer-demo eureka-server zuul-demo UTF-8 UTF-8 1.8 Finchley.SR2 org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import tk.mybatis mapper-spring-boot-starter 2.0.4 mysql mysql-connector-java 5.1.32 org.projectlombok lombok org.springframework.boot spring-boot-maven-plugin
接着创建服务提供方:user-service,pom.xml如下:
springcloud-example com.cn 1.0-SNAPSHOT 4.0.0 user-service org.springframework.boot spring-boot-starter-web mysql mysql-connector-java tk.mybatis mapper-spring-boot-starter org.springframework.cloud spring-cloud-starter-netflix-eureka-client
对应的启动方法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注册中心@EnableDiscoveryClientpublic 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{}
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. */@Servicepublic 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: 8180spring: datasource: url: jdbc:mysql://localhost:3306/ssm_blog username: root password: 85586537 application: name: user-servicemybatis: type-aliases-package: com.cn.pojo#注册到eurekaeureka: 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文件
springcloud-example com.cn 1.0-SNAPSHOT 4.0.0 consumer-demo org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-starter-openfeign
启动类: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 //开启Feignpublic 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. * 调用方直接调用提供方就可以,所以数据库那些不需要了 */@Datapublic 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管理@Componentpublic class UserClientFallBack implements UserClient { @Override public User queryById(Integer id) { User user = new User(); user.setUsername("未知用户"); return user; }}
application.yml配置
server: port: 8280spring: application: name: consumer-service#注册到eurekaeureka: 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文件
springcloud-example com.cn 1.0-SNAPSHOT 4.0.0 eureka-server org.springframework.cloud spring-cloud-starter-netflix-eureka-server
启动类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@EnableEurekaServerpublic class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); }}
application.yml配置具体参考前一篇高可用配置
网关:zuul-demo
pom.xml文件
springcloud-example com.cn 1.0-SNAPSHOT 4.0.0 zuul-demo org.springframework.cloud spring-cloud-starter-netflix-zuul org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.apache.commons commons-lang3
启动类: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@EnableZuulProxypublic 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,如果没有就拦截,有就放行 */@Componentpublic 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/eurekaspring: application: name: zuul-serviceribbon: ConnectionTimeout: 500 ReadTimeout: 1000hystrix: 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/**