从0到1编写注册中心-第一部分:简要实现原理:注册中心跟打车平台有什么区别呢?
作者:互联网
从0到1编写注册中心-提纲目录
1、注册中心简要实现原理
2、如何实现注册与发现?
3、如何实现LoadBlance?
4、如何实现集群Cluster?分布式一致性算法?
5、市面上注册中心对比:Nacos、Eureka、Zookeeper
一、注册中心简要实现原理
1、 主体应用框架演进
1.1 All In One单体应用时代
在最原始的系统设计中,我们通常将系统分为三层,数据访问层、业务逻辑层、视图层也就是我们常说的MVC模型。此架构没有对业务场景进行划分,所有业务模块的数据访问层、业务逻辑层、视图层都放在一个工程中,最终经过编译、打包,部署在一台服务器上。
优点:分层设计明确了不同团队的分工,职责清晰、分工明确,诞生了前端团队、后端团队和DBA团队;J2EE开发简单,所有类都直接本地引用使用,事务处理只需要依赖数据库即可。容易部署。
缺点:业务之间的耦合程度逐步变得严重,随着应用扩大,JVM内存消耗大,性能也会逐步下降;随着业务逻辑复杂度增加,人员的流动,导致整个应用会越来越困难。当系统到达一定瓶颈后,水平扩展困难;
1.2 垂直拆分时代
当流程逐渐增加,可以将应用根据业务拆分成多个应用;拆分完成的应用形成独立对外提供服务的系统,应用之间采用特定的通讯协议(大部分是Http协议)进行调用;垂直拆分从一定程度上缓解了单体应用的臃肿。
但随着时间迁移,逐步发现每个垂直系统在进行调用的过程中协议的不统一(特别是在与不同的供应商提供的系统进行相互调用是),导致开发成本逐步变高,维护难度高。
1.3 分布式时代-早期的SOA
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,以及提供标准的服务协议,使前端应用能更快速的响应多变的市场需求。
面向服务架构(SOA)将单一进程的应用进行拆分,形成独立对外提供服务的组件,每个组件通过网络协议对外提供服务。
SOA特点:
A、明确的协议
B、明确的接口
典型的SOA实现:ESB(企业服务总线);服务之间的通讯和调用均通过ESB进行完成。
ESB负责服务之间消息的解析、转换、路由,统一编排信息处理流程等。
优点:各个服务系统之间形成统一的交互协议;
但也有2个比较明显的缺点:
A、ESB逐步成为性能瓶颈,因为所有的交互都经过ESB进行转发,当ESB出现故障时,整个服务系统之间的调用将瘫痪,很容易形成单点问题;
B、服务之间的调用将产生2次调用,性能比较浪费。
第一次调用:服务调用者访问ESB
第二次调用:ESB调用访问提供者,然后进行转换消息内容转换。
1.4 分布式时代-微服务
为了解决ESB 比较明显缺点,服务化架构得到进一步演进,逐步形成颗粒度更细的微服务化。微服务化通过注册中心来解决ESB对应的单点问题;同时让服务消费者直接与服务提供者进行通讯,减少服务之间的交互次数(由原来的2次变成1次)。
典型的微服务化架构:Dubbo、SpringCloud。
2、用生活中的例子形容注册中心职责
用一个生活中经常使用到的打车App做一个比方。
在没有滴滴等打车平台出现之前,因为市民不知道什么时候会有空的车辆,所以市民需要打车使用车需要站在路边扬手拦车。就好比原来一个应用需要调用其他应用接口时,需要提前知晓并配置好应用接口地址,当应用变更地址时,需要手动更改接口地址,给运维带来了不便。
为了解决市民打车难的问题,有了滴滴平台。
A、上班打卡
出租车司机上班的时候,告知滴滴平台可以开始接单了,等待市民的下单。
好比:注册中心的注册服务。
B、发布打车需求
市民需要打车时,发布需求到滴滴平台,滴滴平台根据需要找到对应的出租车,并告知市民和司机对应的联系方式,后续均由市民和司机自行联系。
好比:服务调用者通过注册中心获取对应的服务提供者信息。
C、市民司机相互联系确认地址
市民司机均知道对方联系方式后,电话联系确认地址
好比:服务调用者知晓服务提供者后,通过IP服务地址进行调用服务
D、司机因故不能执行
当司机因故不能执行任务时,滴滴平台自动更换新的司机信息告知市民
好比:应用地址发生变更时,可自行获取到最新的地址信息
E、市民取消行程
市民因故取消行程,告知滴滴平台行程取消,订单取消。
好比:服务调用者发生故障,注册中心告知服务提供者:调用者发生变化了
3、根据注册中心职责设计注册中心功能
A、服务注册
服务提供者通过注册接口将服务ID、IP-URL地址告知注册中心
B、服务获取
服务调用者获取对应需要调用服务的地址信息
C、服务调用
服务调用者根据服务地址进行调动提供者接口
D、服务提供者掉线异常
当服务提供者发生异常掉线后,注册中心需要知晓,并及时通知服务调用者地址发生变化
E、服务调用者掉线异常
当服务调用者发生异常掉线后,注册中心需要知晓。
2、如何实现注册与发现?
Q:注册中心中的服务提供者需要实例化存储么?
A:一般都采用本地化文件实例化存储,很少使用Db进行实例化存储。
Q:使用什么通讯协议实现服务提供者/调用者与注册中心通讯?
A:一般采用Http协议或者TCP/IP协议。TCP/IP协议在监听和通知服务提供者/调用者方面有更好的优势,但对服务器的要求比较高。
本文采用Http协议进行通讯。
Q:服务提供者/调用者如何实现异常通知?
A:服务提供者/调用者发生掉线时,一般采用心跳模式告知存活或者通过TCP/IP长连接模式。需要考虑:服务提供者/调用者无响应、网络异常导致闪断;
2.1 注册中心提供注册服务
package com.test.controller; import com.test.storage.CacheStorage; import com.test.storage.ServiceInfo; import org.apache.catalina.util.ServerInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @RestController public class RegistryController { private CacheStorage storage; @Autowired public RegistryController(CacheStorage storage) { this.storage = storage; } @RequestMapping("/registerService") public String registerService(String serviceId, String address) { this.storage.addServiceInfo(serviceId, address); return "ok"; } @RequestMapping("/getServiceInfo") public List<String> getServiceInfo(String serviceId) { List<ServiceInfo> serviceInfoList = this.storage.getServiceInfo(serviceId); List<String> result = new ArrayList<>(); for (ServiceInfo serviceInfo : serviceInfoList) { result.add(serviceInfo.toString()); } return result; } @RequestMapping("/heartbeat") public String heartbeat(String serviceId, String address) { this.storage.updateService(serviceId, address); return "ok"; } }
-- 注册服务存储服务
package com.test.storage; import org.springframework.stereotype.Component; import java.util.*; @Component public class CacheStorage { private Map<String, List<ServiceInfo>> serviceMap; public CacheStorage() { this.serviceMap = new HashMap<>(); } public void addServiceInfo(String serviceId, String address) { ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setServiceId(serviceId); serviceInfo.setAddress(address); serviceInfo.setLastUpdateTime(new Date()); List<ServiceInfo> existAddressList = this.serviceMap.getOrDefault(serviceId, null); if (existAddressList == null) { existAddressList = new ArrayList<>(); this.serviceMap.put(serviceId, existAddressList); } existAddressList.add(serviceInfo); } public void removeServiceInfo(String serviceId, String address) { List<ServiceInfo> existAddressList = this.serviceMap.getOrDefault(serviceId, null); if (existAddressList != null) { ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setServiceId(serviceId); serviceInfo.setAddress(address); existAddressList.remove(address); } } public List<ServiceInfo> getServiceInfo(String serviceId) { return this.serviceMap.getOrDefault(serviceId, new ArrayList<>()); } public void updateService(String serviceId, String address) { ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setServiceId(serviceId); serviceInfo.setAddress(address); serviceInfo.setLastUpdateTime(new Date()); List<ServiceInfo> existAddressList = this.serviceMap.getOrDefault(serviceId, null); if (existAddressList != null) { int index = existAddressList.indexOf(serviceInfo); if (index >= 0) { existAddressList.get(index).setLastUpdateTime(new Date()); } } } }
2.2 客户端注册服务
package com.test.registry; import java.util.List; public interface IServiceRegistry { String registerService(String serviceId, String address); void heartBeat(String serviceId, String address); List<String> getServiceInfoList(String serviceId); } package com.test.registry; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class DefaultServiceRegistry implements IServiceRegistry { private String registerCenterAddress = ""; public DefaultServiceRegistry(String registerCenterAddress) { this.registerCenterAddress = registerCenterAddress; } @Override public String registerService(String serviceId, String address) { RestTemplate restTemplate = new RestTemplate(); MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("serviceId", serviceId); params.add("address", address); return restTemplate.postForObject(this.registerCenterAddress + "/registerService", params, String.class); } // 心跳更新服务信息 @Override public void heartBeat(String serviceId, String address) { Runnable runnable = new Runnable() { @Override public void run() { while (true) { System.out.println("Start HeartBeat"); try { Thread.sleep(10000); RestTemplate restTemplate = new RestTemplate(); MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("serviceId", serviceId); params.add("address", address); List<String> serviceInfoList = new ArrayList<>(); restTemplate.postForObject(registerCenterAddress + "/heartbeat", params, String.class); } catch (InterruptedException e) { e.printStackTrace(); } } } }; Thread thread = new Thread(runnable); thread.start(); } @Override public List<String> getServiceInfoList(String serviceId) { RestTemplate restTemplate = new RestTemplate(); MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("serviceId", serviceId); List<String> serviceInfoList = new ArrayList<>(); serviceInfoList = restTemplate.postForObject(this.registerCenterAddress + "/getServiceInfo", params, serviceInfoList.getClass()); return serviceInfoList; } }
标签:简要,String,中心,import,serviceId,注册,address,new,public 来源: https://www.cnblogs.com/fenggetop/p/16414719.html