(Eureka服务端集群+Eureka客户端集群+RestTemplate通信+Ribbon负载均衡[集成Hystrixs]+Feign[开启Hystrix]+Zuul+CloudConfig)
SpringCloud
项目结构
- springcloud-parent(父级模块,用于管理整体项目jar包)
- springcloud-commons(存放项目中模拟假数据的实体类)
- springcloud-config-server-1070(Config服务端,用于与gitee仓库连接拉取远端yml,properties文件)
- springcloud-eureka-server-1010(Eureka服务端,用于应用的注册与发现,会做集群)
- springcloud-order-server-1030(Eureka客户端,模拟分布式开发模块间通信,实现Ribbon集成Hystrix)
- springclpud-pay-server-1040(Eureka客户端,模拟分布式开发模块间通信,实现Feign开启Hystrix)
- springcloud-user-server-1020(Eureka客户端,模拟数据提供模块)
- springcloud-zuul-server-1050(Zuul网关,实现过滤器权限控制)
父类模块中需要继承springboot与管理springcloud
继承
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
管理
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>hi.same</groupId>
<artifactId>springcloud-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
Eureka
服务端
单体服务端
导入Eureka服务端依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
在SpringBoot主类上打上@EnableEurekaServer注解,然后配置application.yml文件
server:
port: 1010 #此处为springboot服务端口号
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false #取消注册到EurekaServer
fetch-registry: false #不拉取服务的通信地址
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #客户端的注册地址
开启springboot服务,访问localhost:1010进入到eureka服务端页面
服务端集群
根据上面的单体服务端修改application.yml配置多套spring环境
eureka:
client:
service-url:
defaultZone: http://peer1:1011/eureka/,http://peer2:1012/eureka/,http://peer3:1013/eureka/
instance:
prefer-ip-address: true
spring:
profiles:
active: peer3
application:
name: eureka-server
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
instance-id: eureka-server:1011
server:
port: 1011
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
instance-id: eureka-server:1012
server:
port: 1012
---
spring:
profiles: peer3
eureka:
instance:
hostname: peer3
instance-id: eureka-server:1013
server:
port: 1013
需要注意此处要把刚才配置的取消自身注册与拉取通信地址删除掉,因为默认为开启,修改springs.profiles.active分别运行三次就可以完成Eureka服务端的集成了,如果无法重复运行三次springboot主配置文件则需要在运行配置里面设置
如果idea版本过低则需要去掉小勾
客户端
模拟数据公共类
在springcloud-commons中编写User实体类,并在父级模块中添加管理。
public class User {
private Long id;
private String username;
private String password;
public User() {
}
public User(Long id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
单体客户端
导入相关jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>hi.same</groupId>
<artifactId>springcloud-commons</artifactId>
</dependency>
在SpringBoot主类上添加@EnableEurekaClient注解,修改application.yml文件
eureka:
client:
service-url:
defaultZone: http://peer1:1011/eureka/,http://peer2:1012/eureka/,http://peer3:1013/eureka/ #eureka服务端的注册地址
instance:
instance-id: user-server:1020 #注册服务的ID
prefer-ip-address: true #使用ip注册到eureka
server:
port: 1020
spring:
application:
name: user-server #应用名
运行服务,访问之前的localhost:1010发现服务已经注册成功
客户端集群
和Eureka服务端操作相似,配置多套spring运行环境然后运行多次springboot服务即可
eureka:
client:
service-url:
defaultZone: http://peer1:1011/eureka/,http://peer2:1012/eureka/,http://peer3:1013/eureka/ #eureka服务端的注册地址
instance:
prefer-ip-address: true #使用ip注册到eureka
spring:
application:
name: user-server #应用名
profiles:
active: user-server:1022
---
eureka:
instance:
instance-id: user-server:1021 #注册服务的ID
server:
port: 1021
spring:
profiles: user-server:1021
---
eureka:
instance:
instance-id: user-server:1022 #注册服务的ID
server:
port: 1022
spring:
profiles: user-server:1022
order与pay的Eureka客户端不需要做集群,因为要用来实现Ribbon以及Feign。
RestTemplate实现客户端间通信
在user客户端中编写controller用于提供假数据给order和server
@RestController
public class UserController {
@Value("${server.port}")
private String port;
@RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
public User getById(@PathVariable("id") Long id){
return new User(id,"ls",port);
}
}
order客户端的springboot主类中编写RestTemplate的Bean方便用于自动注入,再编写controller用于获取user客户端中的数据。
springboot主类
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
controller
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
public User getById(@PathVariable("id") Long id){
String url = "http://localhost:1020/user/"+id;
return restTemplate.getForObject(url,User.class);
}
}
访问localhost:1030/order/1返回数据
负载均衡
Ribbon实现
导入ribbon包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
在springboot主类的RestTemplate的Bean配置上加上@LoadBlanced注解
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
再将controller中访问url的localhost:port改为user-server
@RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
public User getById(@PathVariable("id") Long id){
String url = "http://user-server/user/"+id;
return restTemplate.getForObject(url,User.class);
}
Feign实现
导入jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
并在springboot主类上打上@EnableFeignClient标签,然后编写feignclient
@FeignClient(value = "user-server")
public interface UserFeignClient {
@RequestMapping(value = "/user/{id}",method = RequestMethod.GET,consumes = "application/json")
User getById(@PathVariable("id") Long id);
}
FeignClient指的是当前Feign客户端是针对哪个服务进行的负载均衡,接口中方法要与对应服务的 controller中方法一致(返回值,参数,请求方式,方法名,请求地址)
熔断器Hystrix
Ribbon集成熔断
导入jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在springboot主配置类上打上@EnableCircuitBreaker注解开启熔断器,然后在controller的方法上打上@HystrixCommand标签,并编写降级方法
@RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
@HystrixCommand(fallbackMethod = "getByIdFallbackMethod")
public User getById(@PathVariable("id") Long id){
String url = "http://user-server/user/"+id;
return restTemplate.getForObject(url,User.class);
}
public User getByIdFallbackMethod(@PathVariable("id") Long id){
return new User(-1L,"报错","正在殴打程序员");
}
其中注解中的fallbackMethod值要与降级方法的方法名一致,并且降级方法的返回值参数要与熔断方法一致。
Feign开启熔断
由于Feign的jar包已经集成了熔断器所以直接配置application.yml开启熔断即可
feign:`在这里插入代码片`
hystrix:
enabled: true #开启熔断支持
ribbon:
ReadTimeout: 300000
SocketTimeout: 300000
ConnectTimeout: 30000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 10300
实现FeignClient接口编写降级方法
@Component
public class UserFeignClientFallback implements UserFeignClient {
@Override
public User getById(Long id) {
return new User(-1L,"出错","正在殴打程序员");
}
}
重写Client中的方法返回兜底数据即可
实现FallbakFactory接口编写降级方法
@Component
public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {
@Override
public UserFeignClient create(Throwable throwable) {
return new UserFeignClient() {
@Override
public User getById(Long id) {
throwable.printStackTrace();
return new User(-1L,"出错","殴打程序员");
}
};
}
}
实现工厂接口时泛型为Client接口泛型,重写方法后要求返回一个接口对象则使用匿名内部类的方法重写接口方法实现降级方法的编写并返回兜底数据
Zuul网关
请求分发配置
导入jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>hi.same</groupId>
<artifactId>springcloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
在springboot主类上打上@EnableZuulProxy注解开启网关配置并编写application.yml文件
client:
service-url:
defaultZone: http://peer1:1011/eureka/,http://peer2:1012/eureka/,http://peer3:1013/eureka/ #eureka服务端的注册地址
instance:
instance-id: zuul-server:1050 #注册服务的ID
prefer-ip-address: true #使用ip注册到eureka
server:
port: 1050
spring:
application:
name: zuul-server #应用名
zuul:
prefix: "/servers" #统一访问前缀
ignoredServices: "*" #禁用掉使用浏览器通过服务名的方式访问服务
routes:
pay-server: "/pay/**" #指定pay-server这个服务使用 /pay路径来访问 - 别名
order-server: "/order/**"
过滤器权限控制配置
@Component
public class LoginFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return FilterConstants.DEBUG_FILTER_ORDER;
}
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String uri = request.getRequestURI();
if(uri.contains("/login") || uri.contains("/register")){
return false;
}
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
HttpServletResponse response = requestContext.getResponse();
response.setContentType("application/json;charset=utf-8");
try{
request.setCharacterEncoding("utf-8");
}catch (Exception e){
e.printStackTrace();
}
String token = request.getHeader("token");
if(!StringUtils.hasLength(token)){
Map<String,Object> jsonResult = new HashMap<>();
jsonResult.put("success",false);
jsonResult.put("message","没登陆");
String json = JSON.toJSONString(jsonResult);
try{
response.getWriter().print(json);
}catch (Exception e){
e.printStackTrace();
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
requestContext.setSendZuulResponse(false);
}
return null;
}
}
filterType为过滤器类型,一般编写的都是pre类型,filterOrder为过滤器优先级,可以决定同类型过滤器的执行顺序,sholdFilter返回true则执行run方法,返回false不执行run方法,run方法中则是具体的过滤逻辑代码
CloudConfig配置
服务端配置
将模块中的application.yml修改为对应名字传到gitee远端仓库中,例如user-server的配置文件命名为application-user.yml
导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
在springboot主类上打上@EnableConfigServer注解开启配置中心服务端配置,并编写application.yml文件
eureka:
client:
service-url:
defaultZone: http://peer1:1011/eureka/,http://peer2:1012/eureka/,http://peer3:1013/eureka/ #eureka服务端的注册地址
instance:
instance-id: config-server:1070 #注册服务的ID
prefer-ip-address: true #使用ip注册到eureka
spring:
application:
name: config-server #应用名
cloud:
config:
server:
git:
uri: https://gitee.com/HisameKenshin/springcloud-config.git
username: 17360074797
password: izayoi16.
server:
port: 1070
需要注意uri为远端gitee仓库uri,username为gitee登陆账户,password为登陆密码
客户端配置
以zuul-server为例,导入jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
在resources下编写bootstrap.yml文件
spring:
cloud:
config:
label: master
name: application-zuul
discovery:
service-id: config-server
enabled: true
eureka:
client:
service-url:
defaultZone: http://peer1:1011/eureka/,http://peer2:1012/eureka/,http://peer3:1013/eureka/ #eureka服务端的注册地址
开启config-server服务自动发现,并配置要获取的是远端仓库中master分支的application-zuul.yml文件