Spring Cloud是基于Spring Boot的一整套实现微服务的框架,可以说,Spring Boot作为框架,Spring Cloud作为微服务,一起构成了一种不可忽视的、新生的框架体系。它提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件,方便易用。Spring Cloud包含了非常多的子框架,其中,Spring Cloud Netflix是其中一套框架,它主要提供的模块包括:服务发现、断路器和监控、智能路由、客户端负载均衡等。
特性
- Eureka实例可以注册和发现使用spring管理的bean
- 嵌入式Eureka服务器可以用声明式的Java配置创建
- Hystrix客户端可以用简单的注解驱动
- Java配置可以启用嵌入的Hystrix指示面板
- 客户端负载均衡
Spring Cloud Netflix组件以及部署
(1)Eureka,服务注册和发现,它提供了一个服务注册中心、服务发现的客户端,还有一个方便的查看所有注册的服务的界面。 所有的服务使用Eureka的服务发现客户端来将自己注册到Eureka的服务器上。
(2)Zuul,网关,所有的客户端请求通过这个网关访问后台的服务。他可以使用一定的路由配置来判断某一个URL由哪个服务来处理。并从Eureka获取注册的服务来转发请求。
(3)Ribbon,即负载均衡,Zuul网关将一个请求发送给某一个服务的应用的时候,如果一个服务启动了多个实例,就会通过Ribbon来通过一定的负载均衡策略来发送给某一个服务实例。
(4)Feign,服务客户端,服务之间如果需要相互访问,可以使用RestTemplate,也可以使用Feign客户端访问。它默认会使用Ribbon来实现负载均衡。
(5)Hystrix,监控和断路器。我们只需要在服务接口上添加Hystrix标签,就可以实现对这个接口的监控和断路器功能。
(6)Hystrix Dashboard,监控面板,他提供了一个界面,可以监控各个服务上的服务调用所消耗的时间等。
(7)Turbine,监控聚合,使用Hystrix监控,我们需要打开每一个服务实例的监控信息来查看。而Turbine可以帮助我们把所有的服务实例的监控信息聚合到一个地方统一查看。
Spring Cloud Netflix组件开发
(1)服务注册与监控中心:
可以参考其中文文档:https://springcloud.cc/spring-cloud-netflix.html
@SpringBootApplication
@EnableEurekaServer
@EnableHystrixDashboard
public class ApplicationRegistry {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
这里使用spring boot标签的 @SpringBootApplication 说明当前的应用是一个spring boot应用。这样我就可以直接用main函数在IDE里面启动这个应用,也可以打包后用命令行启动。当然也可以把打包的war包用tomcat之类的服务器启动。 使用标签 @EnableEurekaServer ,就能在启动过程中启动Eureka服务注册中心的组件。它会监听一个端口,默认是8761,来接收服务注册。并提供一个web页面,打开以后,可以看到注册的服务。 添加 @EnableHystrixDashboard 就会提供一个监控的页面,我们可以在上面输入要监控的服务的地址,就可以查看启用了Hystrix监控的接口的调用情况。 当然,为了使用上面的组件,我们需要在maven的POM文件里添加相应的依赖,比如使用 spring-boot-starter-parent ,依赖 spring-cloud-starter-eureka-server 和 spring-cloud-starter-hystrix-dashboard 等。
(2)服务间调用:
两种方式可以进行服务调用,RestTemplate和FeignClient。不管是什么方式,他都是通过REST接口调用服务的http接口,参数和结果默认都是通过jackson序列化和反序列化。因为Spring MVC的RestController定义的接口,返回的数据都是通过jackson序列化成json数据。
第一种:RestTemplate,只需要定义一个RestTemplate的Bean,设置成 LoadBalanced 即可:
@Configuration
public class SomeCloudConfiguration {
@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
//这样我们就可以在需要用的地方注入这个bean使用:
public class SomeServiceClass {
@Autowired
private RestTemplate restTemplate;
public String getUserById(Long userId) {
UserDTO results = restTemplate.getForObject("http://users/getUserDetail/" + userId, UserDTO.class);
return results;
}
}
其中, users 是服务ID,Ribbon会从服务实例列表获得这个服务的一个实例,发送请求,并获得结果。对象 UserDTO 需要序列号,它的反序列号会自动完成。
第二种:FeignClient
@FeignClient(value = "users", path = "/users")
public interface UserCompositeService {
@RequestMapping(value = "/getUserDetail/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
UserDTO getUserById(@PathVariable Long id);
}
我们只需要使用 @FeignClient 定义一个借口,Spring Cloud Feign会帮我们生成一个它的实现,从相应的users服务获取数据。 其中, @FeignClient(value = "users", path = "/users/getUserDetail") 里面的value是服务ID,path是这一组接口的path前缀。 在下面的方法定义里,就好像设置Spring MVC的接口一样,对于这个方法,它对应的URL是 /users/getUserDetail/{id} 。 然后,在使用它的时候,就像注入一个一般的服务一样注入后使用即可:
public class SomeOtherServiceClass {
@Autowired
private UserCompositeService userService;
public void doSomething() {
// .....
UserDTO results = userService.getUserById(userId);
// other operation...
}
}
(3)断路器:
//断路器:为了解决当某个方法调用失败的时候,调用后备方法来替代失败的方法,已达到容错/阻止级联错误的功能
//fallbackMethod指定后备方法
@HystrixCommand(fallbackMethod = "doStudentFallback")
@RequestMapping(value = "dostudent",method = RequestMethod.GET)
public String doStudent(){
return "your name:secret,your age:secret!";
}
public String doStudentFallback(){
return "your name:FEIFEI,your age:26!";
}
其中,使用@EnableCircuitBreaker来启用断路器支持,Spring Cloud提供了一个控制台来监控断路器的运行情况,通过@EnableHystrixDashboard注解开启。
最后重点讲讲注册中心
基础依赖
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.cloud:spring-cloud-starter')
eureka(单机)
服务端:
依赖 compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-server')
application.yml 配置
spring:
application:
name: dev
eureka:
client:
service-url:
defaultZone: http://本机ip地址:8761/eureka/ #注册中心地址
register-with-eureka: false #表明该实例是否应该与尤里卡服务器登记其信息被别人发现。在某些情况下,您不希望您的实例被发现而你想发现其他实例。默认为true
fetch-registry: false #表明这个客户是否应该从尤里卡服务器获取尤里卡注册表信息。默认为true
server:
port: 8761
启动类添加 @EnableEurekaServer
客户端:
主要依赖 compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
application.yml 配置
eureka:
client:
service-url:
defaultZone: http://eureka服务地址:8761/eureka/
启动类添加 @EnableDiscoveryClient (注册中心通用客户端注解)或 @EnableEurekaClient (只有eureka客户端可用)
eureka(集群)
服务端
主要依赖compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-server')
compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
application.yml 配置
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
server:
enable-self-preservation: false #使用自我保护,默认true
peer-node-read-timeout-ms: 5000 #节点读取超时时间,默认200毫秒
---
spring:
application:
name: eureka1
profiles: eureka1
eureka:
client:
service-url:
defaultZone: http://eureka2:8761/eureka/,http://eureka3:8761/eureka/
instance:
hostname: eureka1
---
spring:
application:
name: eureka2
profiles: eureka2
eureka:
client:
service-url:
defaultZone: http://eureka1:8761/eureka/,http://eureka3:8761/eureka/
instance:
hostname: eureka2
---
spring:
application:
name: eureka3
profiles: eureka3
eureka:
client:
service-url:
defaultZone: http://eureka1:8761/eureka/,http://eureka2:8761/eureka/
instance:
hostname: eureka3
eureka.client.service-url.defaultZone 与 eureka.instance.hostsname eureka1、eureka2、eureka3 都再本机的 hosts 文件里事先写好,
例如 eureka1 服务器上的hosts文件
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
127.0.0.1 eureka1
xxx.xxx.x.xx eureka2
xx.xxx.xxx.xx eureka3
启动类同样是添加 @EnableEurekaServer 注意:如果运行 jar 包时需要程序接收命令行参数,一定要再main方法的 SpringApplication.run() 方法 中传入 args 参数
例如:
public
static
void
main(String[] args) {SpringApplication.run(QnbbsConsumer.class
,args);}
如果不传入的话在命令行输入 java -jar xxx.jar --参数名=参数值 参数将不起作用从而影响你无法选择写好的环境配置
客户端:
依赖跟单机的一样
application.yml 配置
eureka:
instance:
prefer-ip-address: true #是否显示ip
ip-address: 本机ip #填写本机ip地址
client:
service-url:
defaultZone: http://eureka1:8761/eureka/,http://eureka2:8761/eureka/,http://eureka3:8761/eureka/
启动类注解与单机版一样
客户端注册一台注册中心,同时注册到其他集群中说明成功!
eureka添加 spring-security 权限认证
依赖compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-server')
application.yml 配置
server:
port: 8761
spring:
application:
name: eureka-server
security:
user:
name: zyn
password: zyn123...
#用户名与密码若是不配置,系统会自动生成并打印在控制台日志上
eureka:
client:
register-with-eureka: false
fetch-registry: false
server:
enable-self-preservation: false
peer-node-read-timeout-ms: 5000
management:
endpoints:
web:
base-path: /
exposure:
include: '*'
endpoint:
health:
show-details: always
restart:
enabled: true
server:
port: 8761
---
spring:
profiles: eureka-jy
eureka:
client:
service-url:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@eureka-B:8761/eureka/,http://${spring.security.user.name}:${spring.security.user.password}@eureka-C:8761/eureka/
instance:
hostname: eureka-A
---
spring:
profiles: eureka-lhn
eureka:
client:
service-url:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@eureka-A:8761/eureka/,http://${spring.security.user.name}:${spring.security.user.password}@eureka-C:8761/eureka/
instance:
hostname: eureka-B
---
spring:
profiles: eureka-zsm
eureka:
client:
service-url:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@eureka-A:8761/eureka/,http://${spring.security.user.name}:${spring.security.user.password}@eureka-B:8761/eureka/
instance:
hostname: eureka-C
创建一个类并继承 WebSecurityConfigurerAdapter 类,并在此类上添加 @EnableWebSecurity和@Configuration 注解,重写 configure 方法
package com.iteng.eureka.security;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // 关闭csrf
http.authorizeRequests().anyRequest().authenticated().and().httpBasic(); // 开启认证
}
}
启动类添加注解@EnableEurekaServer @EnableWebSecurity
下线服务
http://eureka地址:端口/eureka/apps/服务名/服务地址:服务端口号
实例如下:
erueka注册中心ip: 10.100.1.100
端口: 12000
服务名: CPS-RISK-SERVER
实例id: 192.168.10.54:18883
则向下面的url通过http发送delete命令,可将指定的服务实例删除:
http://10.100.1.100:12000/eureka/apps/CPS-RISK-SERVER/192.168.10.54:18883
变更服务状态
http://eureka地址:端口/eureka/apps/服务名/服务地址/status?value=${value}
其中${value}的取值为:OUT_OF_SERVICE , DOWN , UP