摘要:服务消费者可以使用多种模型来发现服务。客户端将定期与服务发现层进行通信,并刷新服务实例的缓存。为了达成目的,我们将要学习使用个不同的客户端库,服务消费者可以使用它们来和进行交互。
本篇代码存放于:github
一、服务发现架构服务发现架构通常具有下面 4 个概念:
服务注册:服务如何使用服务发现代理进行注册?
服务地址的客户端查找:服务客户端查找服务信息的方法是什么?
信息共享:如何跨节点共享服务信息?
健康监测:服务如何将它的健康信息传回给服务发现代理?
下图展示了这 4 个概念的流程,以及在服务发现模式实现中通常发生的情况:
通常服务实例都只向一个服务发现实例注册,服务发现实例之间再通过数据传输,让每个服务实例注册到所有的服务发现实例中。
服务在向服务发现实例注册后,这个服务就能被服务消费者调用了。服务消费者可以使用多种模型来"发现"服务。
每次调用服务时,通过服务发现层来获取目标服务地址并进行调用。这种用的比较少,弊端较多。首先是每次服务调用都通过服务发现层来完成,耗时会比直接调用高。最主要的是这种方法很脆弱,消费端完全依赖于服务发现层来查找和调用服务。
更健壮的方法是使用所谓的客户端负载均衡。
如下图所示:
在这个模型中,当服务消费者需要调用一个服务时:
(1)联系服务发现层,获取所请求服务的所有服务实例,然后放到本地缓存中。
(2)每次调用该服务时,服务消费者从缓存中取出一个服务实例的位置,通常这个"取出"使用简单的复制均衡算法,如“轮询”,“随机",以确保服务调用分布在所有实例之间。
(3)客户端将定期与服务发现层进行通信,并刷新服务实例的缓存。
(4)如果在调用服务的过程中,服务调用失败,那么本地缓存将从服务发现层中刷新数据,再次尝试。
二、spring cloud 实战使用 spring cloud 和 Netflix Eureka 搭建服务发现实例。
1、构建 Spring Eureka 服务eurekasvr POM 主要配置如下:
org.springframework.cloud spring-cloud-starter-eureka-server
applicaiton.yml 配置如下:
server: port: 8761 eureka: client: #不注册自己 register-with-eureka: false #不在本地缓存注册表信息 fetch-registry: false server: #接受请求前的等待实际,开发模式下不要开启 #wait-time-in-ms-when-sync-empty: 5
最后在启动类上加入注释@SpringBootApplication即可启动服务中心。服务中心管理页面:http://localhost:8761
2、将服务注册到服务中心这里我们编写一个新服务注册到服务中心,organizationservice:组织服务。并将上一篇的两个服务:confsvr:配置中心服务,licensingservice:授权服务注册到服务中心。
a、confvr 注册首先修改 POM 文件:
org.springframework.cloud spring-cloud-config-server org.springframework.cloud spring-cloud-starter-eureka
然后修改配置文件 application.yml:
server: port: 8888 eureka: instance: #注册服务的IP,而不是服务器名 prefer-ip-address: true client: #向eureka注册服务 register-with-eureka: true #拉取注册表的本地副本 fetch-registry: true service-url: #Eureka服务的位置(如果有多个注册中心,使用,分隔) defaultZone: http://localhost:8761/eureka/ spring: profiles: # 使用文件系统来存储配置信息,需要设置为native active: native application: name: confsvr cloud: config: server: native: # 使用文件来存放配置文件,为每个应用程序提供用逗号分隔的文件夹列表 searchLocations: file:///D:/configFolder/licensingservice,file:///D:/configFolder/organizationservice
最后在启动类加入注解@EnableDiscoveryClient,启动即可在 eureka 管理页面发现。
b、licensingservice 注册首先修改 POM
org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-config-client
然后修改配置文件 bootstrap.yml
spring: application: #指定名称,以便spring cloud config客户端知道查找哪个配置 name: licensingservice profiles: #指定环境 active: dev cloud: config: #设为true便会自动获取从配置中心获取配置文件 enabled: true eureka: instance: prefer-ip-address: true client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8761/eureka/
最后在启动类加入注解@EnableDiscoveryClient,启动即可在 eureka 管理页面发现本服务实例。
c、创建 organizationservice首先在文件夹file:///D:/configFolder/organizationservice下创建两个配置文件:organizationservice.yml,organizationservice-dev.yml,内容分别为:
#organizationservice-dev.yml server: port: 10012
#organizationservice.yml spring: application: name: organizationservice
主要 POM 配置如下:
org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-config-client
然后修改配置文件,bootstrap.yml
spring: application: #指定名称,以便spring cloud config客户端知道查找哪个配置 name: organizationservice profiles: #指定环境 active: dev cloud: config: enabled: true eureka: instance: prefer-ip-address: true client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8761/eureka/
最后在启动类加入注解@EnableDiscoveryClient,启动。
3、使用服务发现来查找服务现在已经有两个注册服务了,现在来让许可证服务调用组织服务,获取组织信息。首先在 organizationservice 服务中的 controller 包中加入一个 controller 类,让它能够响应请求:
//OrganizationController.java @RestController public class OrganizationController { @GetMapping(value = "/organization/{orgId}") public Object getOrganizationInfo(@PathVariable("orgId") String orgId) { Mapdata = new HashMap<>(2); data.put("id", orgId); data.put("name", orgId + "公司"); return data; } }
接下来让许可证服务通过 Eureka 来找到组织服务的实际位置,然后调用该接口。为了达成目的,我们将要学习使用 3 个不同的 Spring/Netflix 客户端库,服务消费者可以使用它们来和 Ribbon 进行交互。从最低级别到最高级别,这些库包含了不同的与 Ribbon 进行交互的抽象封装层次:
Spring DiscoveryClient
启用了 RestTemplate 的 Spring DiscoveryClient
Neflix Feign 客户端
a、使用 Spring DiscoveryClient该工具提供了对 Ribbon 和 Ribbon 中缓存的注册服务最低层次的访问,可以查询通过 Eureka 注册的所有服务以及这些服务对应的 URL。
首先在 licensingservice 的启动类中加入@EnableDiscoveryClient注解来启用 DiscoveryClient 和 Ribbon 库。
然后在 service 包下创建 OrganizationService.java
@Service public class OrganizationService { private static final String SERVICE_NAME = "organizationservice"; private DiscoveryClient discoveryClient; @Autowired public OrganizationService(DiscoveryClient discoveryClient) { this.discoveryClient = discoveryClient; } /** * 使用Spring DiscoveryClient查询 * * @param id * @return */ public Organization getOrganization(String id) { RestTemplate restTemplate = new RestTemplate(); Listinstances = discoveryClient.getInstances(SERVICE_NAME); if (instances.size() == 0) { throw new RuntimeException("无可用的服务"); } String serviceUri = String.format("%s/organization/%s", instances.get(0).getUri().toString(), id); ResponseEntity responseEntity = restTemplate.exchange(serviceUri, HttpMethod.GET , null, Organization.class, id); return responseEntity.getBody(); } }
接着在 controller 包中新建 LicensingController.java
@RestController public class LicensingController { private OrganizationService organizationService; @Autowired public LicensingController(OrganizationService organizationService) { this.organizationService = organizationService; } @GetMapping("/licensing/{orgId}") public Licensing getLicensing(@PathVariable("orgId") String orgId) { Licensing licensing = new Licensing(); licensing.setValid(false); licensing.setOrganization(organizationService.getOrganization(orgId)); return licensing; } }
启动所有项目,访问localhost:10011/licensing/12,可以看到返回如下结果:
{ "organization": { "id": "12", "name": "12公司" }, "valid": false }
在实际开发中,基本上是用不到这个的,除非是为了查询 Ribbon 以获取某个服务的所有实例信息,才会直接使用。如果直接使用它存在以下两个问题:
没有利用 Ribbon 的客户端负载均衡
和业务无关的代码写得太多
b、使用带 Ribbon 功能的 Spring RestTemplate 调用服务这种方法是较为常用的微服务通信机制之一。要启动该功能,需要使用 Spring Cloud 注解@LoadBanced 来定义 RestTemplate bean 的构造方法。方便起见直接在启动类中定义 bean:
#LicensingserviceApplication.java @SpringBootApplication @EnableDiscoveryClient //使用不带Ribbon功能的Spring RestTemplate,其他情况下可删除 public class LicensingserviceApplication { /** * 使用带有Ribbon 功能的Spring RestTemplate,其他情况可删除 */ @LoadBalanced @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(LicensingserviceApplication.class, args); } }
接着 service 包下增加一个类:OrganizationByRibbonService.java
@Component public class OrganizationByRibbonService { private RestTemplate restTemplate; @Autowired public OrganizationByRibbonService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } public Organization getOrganizationWithRibbon(String id) { ResponseEntityresponseEntity = restTemplate.exchange("http://organizationservice/organization/{id}", HttpMethod.GET, null, Organization.class, id); return responseEntity.getBody(); } }
最后就是在 LicensingController.js 中加一个访问路径:
//不要忘记注入OrganizationByRibbonService服务 @GetMapping("/licensingByRibbon/{orgId}") public Licensing getLicensingByRibbon(@PathVariable("orgId") String orgId) { Licensing licensing = new Licensing(); licensing.setValid(false); licensing.setOrganization(organizationService.getOrganization(orgId)); return licensing; } }
访问localhost:10011/licensingByRibbon/113,即可看到结果。
c、使用 Netflix Feign 客户端调用Feign 客户端是 Spring 启用 Ribbon 的 RestTemplate 类的替代方案。开发人员只需定义一个接口,然后使用 Spring 注解来标注接口,即可调用目标服务。除了编写接口定义无需编写其他辅助代码。
首先启动类上加一个@EnableFeignClients注解启用 feign 客户端。然后在 POM 中加入 Feign 的依赖
org.springframework.cloud spring-cloud-starter-feign
然后在 client 包下新建 OrganizationFeignClient.java
@FeignClient("organizationservice")//使用FeignClient注解指定目标服务 public interface OrganizationFeignClient { /** * 获取组织信息 * * @param orgId 组织id * @return Organization */ @RequestMapping(method = RequestMethod.GET, value = "/organization/{orgId}", consumes = "application/json") Organization getOrganization(@PathVariable("orgId") String orgId); }
最后修改LicensingController.java,加入一个路由调用 Feign。
//注入OrganizationFeignClient,使用构造注入 @GetMapping("/licensingByFeign/{orgId}") public Licensing getLicensingByFeign(@PathVariable("orgId") String orgId) { Licensing licensing = new Licensing(); licensing.setValid(false); licensing.setOrganization(organizationFeignClient.getOrganization(orgId)); return licensing; }
访问localhost:10011/licensingByFeign/11313,即可看到结果。
总结这一节磨磨蹭蹭写了好几天,虽然例子很简单,但是相信应该是能够看懂的。由于篇幅原因代码没有全部贴上,想要查看完整代码,可以访问这个链接:点击跳转。
本篇原创发布于:http://tapme.top/blog/detail/2018-11-22-15-57
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/77735.html
摘要:实现配置和注册中心最近,阿里开源的比较火,可以和和共用,对升级到非常的方便。只需要添加依赖,使用配置注册中心地址即可。配置不生效,没有使用注解刷新配置分清注册中心和配置中心是两个概念,需要配置两个地址学会看源码,看维基。 Springcloud-nacos实现配置和注册中心 最近,阿里开源的nacos比较火,可以和springcloud和dubbo共用,对dubbo升级到springc...
摘要:集群系统中的单个计算机通常称为节点,通常通过局域网连接,但也有其它的可能连接方式。这样就高兴了,可以专心写自己的,前端就专门交由小周负责了。于是,小周和就变成了协作开发。都是为了项目正常运行以及迭代。 一、前言 只有光头才能变强 认识我的朋友可能都知道我这阵子去实习啦,去的公司说是用SpringCloud(但我觉得使用的力度并不大啊~~)... 所以,这篇主要来讲讲SpringClou...
摘要:集群系统中的单个计算机通常称为节点,通常通过局域网连接,但也有其它的可能连接方式。这样就高兴了,可以专心写自己的,前端就专门交由小周负责了。于是,小周和就变成了协作开发。都是为了项目正常运行以及迭代。 一、前言 只有光头才能变强 认识我的朋友可能都知道我这阵子去实习啦,去的公司说是用SpringCloud(但我觉得使用的力度并不大啊~~)... 所以,这篇主要来讲讲SpringClou...
阅读 1384·2021-09-22 10:02
阅读 1867·2021-09-08 09:35
阅读 4045·2021-08-12 13:29
阅读 2596·2019-08-30 15:55
阅读 2260·2019-08-30 15:53
阅读 2296·2019-08-29 17:13
阅读 2756·2019-08-29 16:31
阅读 2949·2019-08-29 12:24