分布式服务必然要面临的问题:
首先我们来解决第一问题,服务的管理。
来假设一个场景来说话
问题分析
在刚才的案例中,user-service对外提供服务,需要对外暴露自己的地址。而user-consumer(调用者)需要记录服务提供者的地址。将来地址出现变更,还需要及时更新。这在服务较少的时候并不觉得有什么,但是在现在日益复杂的互联网环境,一个项目肯定会拆分出十几,甚至数十个微服务。此时如果还人为管理地址,不仅开发困难,将来测试、发布上线都会非常麻烦,这与DevOps的思想是背道而驰的。
网约车
这就好比是 网约车出现以前,人们出门叫车只能叫出租车。一些私家车想做出租却没有资格,被称为黑车。而很多人想要约车,但是无奈出租车太少,不方便。私家车很多却不敢拦,而且满大街的车,谁知道哪个才是愿意载人的。一个想要,一个愿意给,就是缺少引子,缺乏管理啊。
此时滴滴这样的网约车平台出现了,所有想载客的私家车全部到滴滴注册,记录你的车型(服务类型),身份信息(联系方式)。这样提供服务的私家车,在滴滴那里都能找到,一目了然。
此时要叫车的人,只需要打开APP,输入你的目的地,选择车型(服务类型),滴滴自动安排一个符合需求的车到你面前,为你服务,完美!
Eureka做什么?
Eureka就好比是滴滴,负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。
同时,服务提供方与Eureka之间通过“心跳”
机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。
这就实现了服务的自动注册、发现、状态监控。
Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
提供者:启动后向Eureka注册自己信息(地址,提供什么服务)
消费者:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定期更新
心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态
需要完成的事情:
别头大,耐心看,全部都分为三步走,用脚手架创建项目,开启Eureka,改配置就OK
eureka注册中的服务端和消费端的搭建:
服务端: spring-cloud-starter-netflix-eureka-server
不需要:spring-boot-starter-web
客户端: spring-cloud-starter-netflix-eureka-client
需要:spring-boot-starter-web 服务端和客户单的版本得一致最新版:Greenwich.M3 对应着Spring-boot 2.1.x 稳定版:Finchley.SR2
对应着Spring-boot 2.0.x
1、创建Eureka注册中心,并实现功能
采用Spring initializr脚手架初始化项目
导入maven坐标:spring-cloud-starter-netflix-eureka-server、spring私服
在main函数加上开启eureka服务:@EnableEurekaServer
1,完整的Pom文件:
4.0.0
com.czxy.demo
eureka-demo
0.0.1-SNAPSHOT
jar
eureka-demo
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
2.0.1.RELEASE
UTF-8
UTF-8
1.8
Finchley.RC1
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
false
1.1 编写启动类: @SpringBootApplication
@EnableEurekaServer // 声明这个应用是一个EurekaServer
public class EurekaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaDemoApplication.class, args);
}
}
1.2 编写配置application.yml:
server:
port: 10086 # 端口
spring:
application:
name: eureka-server # 应用名称,会在Eureka中显示
eureka:
client:
register-with-eureka: false # 是否注册自己的信息到EurekaServer,默认是true
fetch-registry: false # 是否拉取其它服务的信息,默认是true
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要加上其它Server的地址。
defaultZone: http://127.0.0.1:${server.port}/eureka
1.3启动服务,并访问:http://127.0.0.1:10086/eureka
2、将user-service注册到Eureka注册中心
先添加SpringCloud依赖:
org.springframework.cloud
spring-cloud-dependencies
Finchley.RC1
pom
import
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
false
然后是Eureka客户端:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
@SpringBootApplication
@EnableEurekaClient // 开启EurekaClient功能
public class UserServiceDemoApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceDemoApplication.class, args);
}
}
```yaml
server:
port: 8090
spring:
datasource:
url: jdbc:mysql://localhost:3308/weifuwu?useUnicode=true&characterEncoding=utf8
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
application:
name: user-service # 应用名称
main:
allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
eureka:
client:
service-url: # EurekaServer地址
defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka(这里搭了一个Eureka集群)
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
eviction-interval-timer-in-ms: 1000 # 扫描失效服务的间隔时间(缺省为60*1000ms)
注意:
main:
allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
3、user-consumer到Eureka注册中心获取服务
导入maven坐标:spring-cloud-starter-netflix-eureka-client、spring私服
org.springframework.cloud
spring-cloud-dependencies
Finchley.RC1
pom
import
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
false
然后是Eureka客户端:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
@SpringBootApplication
@EnableDiscoveryClient
public class UserCustomerApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(UserCustomerApplication.class, args);
}
}
在yml文件中配置eureka服务器的位置等信息
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3308/weifuwu?useUnicode=true&characterEncoding=utf8
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
main:
allow-bean-definition-overriding: true
eureka:
client:
service-url: # EurekaServer地址,多个地址以','隔开
defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
instance:
prefer-ip-address: true # 当其它服务获取地址时提供ip而不是hostname
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
修改远程调用代码,通过DiscoveryClient获取远程服务器的信息,组成请求的URL
必须导入org.springframework.cloud.client.discovery.DiscoveryClient
@Autowired
private DiscoveryClient discoveryClient;
public User queryUserById(Long id){
//1、 根据user-service获取user-serivce 的集群的信息
List instances = discoveryClient.getInstances("user-service");
//2、由于我们没有集群,只有一个,所以直接取出第一个
ServiceInstance instance = instances.get(0);
//3、拼接URL
String url = "http://"+instance.getHost()+":"+instance.getPort()+"/user/"+id;
// 使用restTemplate发起请求
ResponseEntity entity = restTemplate.getForEntity(url, User.class);
// 获取返回对象
User user = entity.getBody();
return user;
}
4、服务默认情况下每隔30s需要向eureka注册中心发送心跳,进行续约,证明我还活着
如果一个服务90s内没有发出心跳,那么eureka就认为他已经宕机了
eureka:
instance:
lease-expiration-duration-in-seconds: 90
lease-renewal-interval-in-seconds: 30