前提:整个项目的微服务集群均写在顶级父工程(le-you)里:便于管理依赖及其版本
服务间调用存在的问题:服务间调用,servicer(提供方)对外提供服务,需要对外暴露自己的地址。而consumer(调用者)需要记录服务提供者的地址。将来地址出现变更,还需要及时更新。不仅开发困难,将来测试、发布上线都会非常麻烦。
1. Eureka
Eureka:负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。同时,服务提供方与Eureka之间通过“心跳”机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。这就实现了服务的自动注册、发现、状态监控。
这就实现了服务的自动注册、发现、状态监控。
架构图:
- Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
- 提供者:启动后向Eureka注册自己信息(地址,提供什么服务)
- 消费者:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定期更新
- 心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态
对于整个乐优项目,我们会在Eureka注册每个微服务,列表如下:
注册微服务(ly–registry)的配置:
server:
port: 10086
spring:
application:
name: ly--registry #应用名称,会在Eureka中显示
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
register-with-eureka: false
fetch-registry: false
注册与拉取服务
注册服务,就是在服务上添加Eureka的客户端依赖,客户端代码会自动把服务注册到EurekaServer中。
- 我们在Item-service中添加Eureka客户端依赖:
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 编写配置
server:
port: 8081
spring:
application:
name: item-service #应用名称
datasource:
url: jdbc:mysql://localhost:3306/yun6
username: root
password: root
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
eureka:
client:
service-url: # EurekaServer地址
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
注意:
- 这里我们添加了spring.application.name属性来指定应用名称,将来会作为应用的id使用。
- 不用指定register-with-eureka和fetch-registry,因为默认是true
在启动类上开启Eureka客户端功能
- 通过添加@EnableDiscoveryClient来开启Eureka客户端功能
@SpringBootApplication
@EnableDiscoveryClient // 开启EurekaClient功能
public class LyItemApplication{
public static void main(String[] args) {
SpringApplication.run(LyItemApplication.class, args);
}
}
消费者从Eureka获取服务
方法与注册类似,只需要在项目中添加EurekaClient依赖,就可以通过服务名称来获取信息了!
服务续约
在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求),告诉EurekaServer:“我还活着”。这个我们称为服务的续约(renew);
有两个重要参数可以修改服务续约的行为:
eureka:
instance:
lease-expiration-duration-in-seconds: 90
lease-renewal-interval-in-seconds: 30
- lease-renewal-interval-in-seconds:服务续约(renew)的间隔,默认为30秒
- lease-expiration-duration-in-seconds:服务失效时间,默认值90秒
也就是说,默认情况下每个30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳,EurekaServer就会认为该服务宕机,会从服务列表中移除,这两个值在生产环境不要修改,默认即可。
2. Ribbon
在实际环境中,我们往往会开启很多个service的集群。此时我们获取的服务列表中就会有多个,一般采用Eureka中已经集成的Ribbon实现负载均衡,在多个实例列表中采用轮询方式进行选择,获取ip和端口来访问。
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.0.0.RC1</version>
</dependency>
3. Hystix
Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现服务间的调用级联失败(雪崩),在微服务中起保护作用。(注:Ribbon的超时时间一定要小于Hystix的超时时间。)
Hystix熔断机制(三种状态):
- closed:断路器关闭,所以请求都正常访问。
- open:如果请求经常超出熔断器设定的阈值(默认情况下,如果在最近的20次请求内,如果有50%的请求都出现了超时,则认为服务有问题,断路器会打开)当用户请求再一次访问这个接口时,会快速返回失败,这时熔断器处于打开状态。熔断器的打开状态默认持续5秒钟(称为休眠时间窗)
- Half open:5秒过后,熔断器处于半开状态,此时它会放一定的请求通过去测试请求是否正常 ,如果请求依然失败,熔断器会再一次进入打开状态,开始休眠时间计时,5秒后,再次进入半开状态,如此循环,直到请求可以正常访问。
- 引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 编写配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 5000 # 熔断超时时长:5000ms
- 开启熔断:
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix //开启Hystrix 功能
public class LyItemApplication{
public static void main(String[] args) {
SpringApplication.run(LyItemApplication.class, args);
}
}
4. Feign
Feign: Rest的远程请求进行隐藏,伪装成类似SpringMVC的Controller一样(类似本地请求)。
Feign 利用SpringMVC的注解来识别请求的信息(请求方式,路径,参数,返回值等)从而帮我们自动的进行一次远程调用,不需要我们再去手动写,一切都交给Feign去做。
- 引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 开启Feign功能:
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableFeignClients // 开启Feign功能
public class LyItemApplication{
public static void main(String[] args) {
SpringApplication.run(LyItemApplication.class, args);
}
}
- 编写接口:
@FeignClient("user-service")
public interface UserFeignClient {
@GetMapping("/user/{id}")
User queryUserById(@PathVariable("id") Long id);
}
- 首先这是一个接口,Feign会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像。
- @FeignClient,类似@Mapper注解。声明这是一个Feign客户端,拿到服务去eureka拉取这个服务(user-service)所对应的服务列表,拿到以后,底层就可以利用ribbon实现负载均衡,去挑选任意一个服务。然后向“/user/{id}”这个地址传递id参数,最后得到的结果自动转成User。
- 接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果。
- 以上feign的客服端不需要我们去做,我们只需要注入这个接口,调用方法就好。
@Service
public class UserService {
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/user/{id}")
public User queryUserById(@PathVariable("id") Long id){
return userFeignClient.queryUserById(id));
}
}
我们看到的仅仅是feign调用了方法,但它的本质是做了一次远程调用。
注:
SpringCloud对Feign进行了增强,使Feign不仅支持SpringMVC的注解,并整合了Ribbon和Eureka,从而使Feign的使用更加方便。
- 因此我们可以删掉ribbon引入依赖。
- Feign默认也有对Hystix的集成,也可以删掉依赖,只不过,默认情况下是关闭的。我们需要通过下面的参数来开启:
feign:
hystrix:
enabled: true # 开启Feign的熔断功能