赞
踩
微服务的其中一个特点是服务之间需要进行网络通信,服务器之间发生调用时调用服务得知道被调用服务的通信地址,试问当微服务数量成百上千之多,程序员该如何管理众多的痛惜地址,对于随时增加的微服务和线下的微服务,又该如何动态的去添加和删除这些微服务的通信地址呢?所以手工管理服务的通信地址时遥不可及的事情,我们需要一个强大的工具来帮我们实现这一功能,所以Eureka应运而生了,同类型的组件还有zookeeper,consul等。
Eureka是一个服务注册和发现组件,简单来说,就是管理微服务通信地址的组件,它包含了EurekaServer服务端(注册中心)和EurekaClient客户端两部分组成,EurekaServer是独立的服务,而EurekaServer需要集成到每一个服务中。EurekaClient在启动时会向EurekaServer提交自己的服务信息(通信地址:如服务名,ip,端口等),在EurekaServer会形成一个微服务的通信地址列表存储起来,这叫服务注册
EurekaClient会定期(RegistryFetchIntervalSeconds:默认30s )从EurekaServer拉取一份微服务通信地址列表缓存到本地。当一个微服务在向另一个微服务发起调用时会根据目标服务的服务名找到其通信地址,然后基于HTTP协议向目标发起请求。这就叫服务发现。
微服务(EurekaClient)采用定时(LeaseRenewalIntervalInSeconds:默认30s)发送“心跳”请求向EurekaServer发请求进行服务续约,其实就是定时向 EurekaServer发请求报告自己的健康状况,告诉EurekaServer自己还活着,不要把自己从服务地址清单中剔除掉,那么当微服务(EurekaClient)宕机未向EurekaServer续约,或者续约请求超时,注册中心机会从服务地址清单中剔除该续约失败的服务。
微服务(EurekaClient)关闭服务前向注册中心发送下线请求,注册中心(EurekaServer)接受到下线请求负责将该服务实例从注册列表剔除
大致流程如图:
springcloud-parent //父项目
pom.xml //父项目的pom
springcloud-eureka-server //注册中心EurekaServer
springcloud-user-server //用户服务EurekaClient ,提供者
springcloud-order-server //订单服务EurekaClient ,消费者
简易的项目结构如下:
SpringCloud的Jar包管理参照文档:https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/multi/multi__quick_start.html#_client_side_usage
springcloud-parent父工程负责管理SpringBoot和SpringCloud的jar包 ,pom.xml如下
在springcloud-parent父工程下面搭建子工程springcloud-eureka-server,然后集成EurekaServer。
相关依赖如下:
提示:spring-cloud-starter-netflix-eureka-server作为EurekaServer端的基础依赖,但同时这个包也把EurekaClient端的以来也导入进来了,spring-boot-starter-web作为web服务的基础依赖是不可缺少的。
启动类配置:
- /
- * 注册中心启动类
- * @EnableEurekaServer : 开启EurekaServer服务端
- */
- @SpringBootApplication
- @EnableEurekaServer
- public class EurekaServerApplication
- {
- public static void main( String[] args )
- {
- SpringApplication.run(EurekaServerApplication.class);
- }
- }
application.yml配置:
- server:
- port: 1010 #端口
- eureka:
- instance:
- hostname: localhost #主机
- client: #客户端配置
- registerWithEureka: false #EurekaServer自己不要注册到EurekaServer自己 ,只有EurekaClient才注册
- fetchRegistry: false #EurekaServer不要拉取服务的通信地址列表 ,只有EurekaClient才拉取地址列表
- serviceUrl: #注册中心的注册地址
- defaultZone: http://localhost:1010/eureka/ #http://${eureka.instance.hostname}:${server.port}/eureka/
- server:
- enable-self-preservation: false #关闭自我保护警告
提示:这里配置了EurekaServer的端口为 1010 ,主机名 localhost ,需要特别说明的是我们再引入EurekaServer的基础依赖spring-cloud-starter-netflix-eureka-server时,这个依赖即引入了EurekaServer所需要的包,也引入了EurekaClient的包,换句话说,现在的springcloud-eureka-server工程既是一个 EurekaServer,也是一个EurekaClient。
我们这里暂时把EurekaClient的功能屏蔽掉 ,即关闭它的服务注册和发现功能,让他做好EurekaServer该做的事情即可。
serviceUrl是服务注册地址,EurekaClient需要注册到EurekaServer就得跟上该地址。
registerWithEureka=false :禁止自己向自己注册
fetchRegistry=false : 禁止拉取服务注册列表
启动测试:
启动springcloud-eureka-server-1010工程,浏览器访问 http://localhost:1010 ,出现如下界面代码EurekaServer集成成功:
参照官方文档 : https://cloud.spring.io/spring-cloud-static/Greenwich.SR5/multi/multi__service_discovery_eureka_clients.html#netflix-eureka-client-starter
修改springcloud-user-server,导入EurekaClient基础依赖:spring-cloud-starter-netflix-eureka-client,导入web的基础依赖:spring-boot-starter-web,相关依赖如下:
<dependencies>
<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>
</dependencies>
启动类配置:
- /
- * 用户的启动类
- * @EnableEurekaClient: 标记该应用是 Eureka客户端
- */
- @SpringBootApplication
- @EnableEurekaClient
- public class UserServerApplication
- {
-
- public static void main( String[] args )
- {
- SpringApplication.run(UserServerApplication.class);
- }
- }
application.yml配置:
- #注册到EurekaServer
- eureka:
- client:
- serviceUrl:
- defaultZone: http://localhost:1010/eureka/
- spring:
- application:
- name: user-server
- server:
- port: 1020
启动测试:
启动springcloud-eureka-server-1010 , 启动springcloud-user-server-1020 , 浏览器再次访问http://localhost:1010,那你应该可以看到我们的user-server服务已经被注册到EurekaServer。如下:
使用id进行注册:
默认情况下EurekaClient使用hostname进行注册到EurekaServer,我们希望使用ip进行注册,可以通过配置eureka.instance.prefer-ip-address=true来指定,同时为了方便区分和管理服务实例,我们指定服务的实例ID,通过eureka.instance.instance-id为user-serer:1020来指定,具体配置如下:
- #注册到EurekaServer
- eureka:
- client:
- serviceUrl:
- defaultZone: http://localhost:1010/eureka/
- instance:
- prefer-ip-address: true #使用ip地址进行注册
- instance-id: user-server:1020 #实例ID
- spring:
- application:
- name: user-server
- server:
- port: 1020
重启客户端,会发现实例Id发生了变化:
重启之前:
重启后:
和搭建user客户端差不多
相关依赖:
<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>
启动类配置:
- /
- * 订单的启动类
- * @EnableEurekaClient: 标记该应用是 Eureka客户端
- */
- @SpringBootApplication
- @EnableEurekaClient
- public class OrderServerApplication
- {
-
- public static void main( String[] args )
- {
- SpringApplication.run(OrderServerApplication.class);
- }
- }
application.yml配置:
- eureka:
- client:
- serviceUrl:
- defaultZone: http://localhost:1010/eureka/
- instance:
- prefer-ip-address: true #使用ip地址进行注册
- instance-id: order-server:1030 #实例ID
- spring:
- application:
- name: order-server
- server:
- port: 1030
测试:
启动订单服务,访问Eureka Server的监控界面:http://localhost:1010,可以看到订单服务也注册进去了。
目前除了Eureka Server以外我们的微服务有订单服务springcloud-order-server,和用户服务springcloud-user-server, 我们就用这两个服务来演示微服务的通信,他们的调用关系应该是:浏览器 -> 订单服务 -> 用户服务,如下图:
这里订单服务通过RestTemplate向用户服务发起调用,目的是要获取到用户服务返回的User对象,最终是需要浏览器获取到User。
用户服务需要提供User对象,我们需要为其编写Controller接口,编写相关方法返回User,订单服务需要从用户服务获取到User对象,而浏览器需要访问订单服务获取到User对象,所以订单服务也需要编写Controller接口供浏览器来调用。
我们发现不管是用户服务还是订单服务都需要用到User对象,那么是不是在用户服务和订单服务都需要创建User的模型?当然没必要,公共的东西就是要抽取,所以我们会把User对象封装在一个公共的模块 springcloud-user-common中然后让用户服务和订单服务都去依赖这个模块。
微服务的通信协议主流的有RPC,Http,SpringCloud是基于Http Restful 风格 ,在Java中发起一个Http请求的方式很多,比如 Apache的HttpClient , OKHttp等等 。Spring为我们封装了一个基于Restful的使用非常简单的Http客户端工具 RestTemplate ,我们就用它来实订单服务和用户服务的通信。需要注意的是,RestTmplate本身不具备服务发现和负载均衡器的功能,我们本章的案例只是演示在订单服务中使用RestTemplate基于ip和端口的方式向用户服务发起调用,即:不走注册中心,不使用服务发现方式。
在springcloud-user-common中创建User对象如下 :
- package cn.itsource.springboot.domain;
-
- public class User {
- private Long id;
- private String username;
- private String desc;
-
- public User() {
- }
-
- public User(Long id, String username, String desc) {
- this.id = id;
- this.username = username;
- this.desc = desc;
- }
-
- 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 getDesc() {
- return desc;
- }
-
- public void setDesc(String desc) {
- this.desc = desc;
- }
- }

用户和订单依赖User模块
<dependency>
<groupId>cn.itsource.springboot</groupId>
<artifactId>springcloud-user-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
修改 springcloud-user-server-1020 工程,编写controller,返回User对象
- //用户服务:暴露接口给订单访问
- @RestController
- public class UserController {
-
-
-
- //订单服务来调用这个方法 http://localhost:1020/user/10
- // @GetMapping(value = "/user/{id}" )
- @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
- public User getById(@PathVariable("id")Long id){
- //根据id去数据库查询User
-
- return new User(id,"zs:"+id,"我是zs");
- }
- }
在订单服务中需要使用RestTemplate调用用户服务,我们需要把RestTmplate配置成Bean方便使用(当然也可以不创建Bean,用的时候直接new对象也可以) ,修改工程springcloud-order-server在主配置类配置RestTemplate如下:
-
- /**
- * 订单的启动类
- */
- @SpringBootApplication
- @EnableEurekaClient
- public class OrderServerApplication
- {
-
- //配置一个RestTemplate ,Spring封装的一个机遇Restful风格的http客户端 工具
- @Bean
- public RestTemplate restTemplate(){
- return new RestTemplate();
- }
-
- public static void main( String[] args )
- {
- SpringApplication.run(OrderServerApplication.class);
- }
- }

创建controller,通过RestTemplate调用用户服务,代码如下:
- //订单服务
- @RestController
- public class OrderController {
-
- //需要配置成Bean
- @Autowired
- private RestTemplate restTemplate ;
-
- //浏览器调用该方法
- @RequestMapping(value = "/order/{id}",method = RequestMethod.GET)
- public User getById(@PathVariable("id")Long id){
- //发送http请求调用 user的服务,获取user对象 : RestTemplate
- //目标资源路径:user的ip,user的端口,user的Controller路径
- String url = "http://localhost:1020/user/"+id;
-
- //发送http请求
- return restTemplate.getForObject(url, User.class);
-
- }
- }

这里的url是用户服务获取user资源的路径,通过RestTmplate发起Http调用,拿到User对象后返回给浏览器。
依次启动Eureka Server注册中心(不启动也行) , 用户服务 ,订单服务 , 浏览器访问订单服务:http://localhost:1030/order/1 , 返回结果:
但是这种方式存在一个问题,欢迎在评论区讨论喔!!!
小tips:涉及到微服务的另一个组件!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。