# [前言](#前言) 前置知识。阅读spring、springboot、spring cloud三者的源码必须严格按照 spring => springboot => spring cloud 的顺序进行,阅读spring cloud源码的必备前置知识: - spring,推荐本人写的简化版的spring框架 [**mini-spring**](https://github.com/DerekYRC/mini-spring/blob/main/README_CN.md) 。熟悉spring源码,阅读springboot源码会非常轻松。 - springboot,重点掌握:1、启动流程 2、**自动装配的原理! 自动装配的原理!! 自动装配的原理!!!** 推荐文章: - [Spring Boot精髓:启动流程源码分析](https://www.cnblogs.com/java-chen-hao/p/11829344.html) - [细说SpringBoot的自动装配原理](https://blog.csdn.net/qq_38526573/article/details/107084943) - [Spring Boot 自动装配原理](https://www.cnblogs.com/javaguide/p/springboot-auto-config.html) 关于spring cloud。spring cloud是构建通用模式的分布式系统的工具集,通过[**spring-cloud-commons**](https://github.com/spring-cloud/spring-cloud-commons) 定义了统一的抽象API,相当于定义了一套协议标准,具体的实现需要符合这套协议标准。spring cloud官方整合第三方组件Eureka、Ribbon、Hystrix等实现了spring-cloud-netflix,阿里巴巴结合自身的Nacos、Sentinel等实现了spring-cloud-alibaba。本项目基于spring-cloud-commons的协议标准自主开发或整合第三方组件提供具体的实现。 写作本项目的目的之一是降低阅读原始spring cloud源码的难度。希望掌握本项目讲解的内容之后再阅读原始spring-cloud的源码能起到事半功倍的效果,所以本项目的功能实现逻辑及原理和官方保持一致但追求代码最大精简化。 技术能力有限且文采不佳,大家可以在此[**issue**](https://github.com/DerekYRC/mini-spring-cloud/issues/1) 留言提问题和发表建议,也欢迎Pull Request完善此项目。 # [服务注册](#服务注册) > 分支: service-registry 为了演示,写一个非常简单的单机版的服务注册和发现中心,命名图图 ```java @RestController @SpringBootApplication public class TutuServerApplication { private static Logger logger = LoggerFactory.getLogger(TutuServerApplication.class); private ConcurrentHashMap> serverMap = new ConcurrentHashMap<>(); public static void main(String[] args) { SpringApplication.run(TutuServerApplication.class, args); } /** * 服务注册 * * @param serviceName * @param ip * @param port * @return */ @PostMapping("register") public boolean register(@RequestParam("serviceName") String serviceName, @RequestParam("ip") String ip, @RequestParam("port") Integer port) { logger.info("register service, serviceName: {}, ip: {}, port: {}", serviceName, ip, port); serverMap.putIfAbsent(serviceName.toLowerCase(), Collections.synchronizedSet(new HashSet<>())); Server server = new Server(ip, port); serverMap.get(serviceName).add(server); return true; } /** * 服务注销 * * @param serviceName * @param ip * @param port * @return */ @PostMapping("deregister") public boolean deregister(@RequestParam("serviceName") String serviceName, @RequestParam("ip") String ip, @RequestParam("port") Integer port) { logger.info("deregister service, serviceName: {}, ip: {}, port: {}", serviceName, ip, port); Set serverSet = serverMap.get(serviceName.toLowerCase()); if (serverSet != null) { Server server = new Server(ip, port); serverSet.remove(server); } return true; } /** * 根据服务名称查询服务列表 * * @param serviceName * @return */ @GetMapping("list") public Set list(@RequestParam("serviceName") String serviceName) { Set serverSet = serverMap.get(serviceName.toLowerCase()); logger.info("list service, serviceName: {}, serverSet: {}", serviceName, JSON.toJSONString(serverSet)); return serverSet != null ? serverSet : Collections.emptySet(); } /** * 查询所有服务名称列表 * * @return */ @GetMapping("listServiceNames") public Enumeration listServiceNames() { return serverMap.keys(); } /** * 服务 */ public static class Server { private String ip; private Integer port; //Construct、Getters、equals、hashCode } } ``` 配置application.yml: ```yaml server: port: 6688 ``` spring-cloud-commons服务注册相关API: ![](./assets/service-registry-api.png) - ServiceInstance和Registration,表示系统中服务的实例 - ServiceRegistry,服务注册和注销接口 - AbstractAutoServiceRegistration,自动注册和注销服务。监听WebServerInitializedEvent(Web服务启动完毕事件),WebServerInitializedEvent触发时注册服务实例;@PreDestroy注解修饰的方法注销服务实例。 服务注册功能实现: TutuDiscoveryProperties,配置服务注册中心地址: ```java @ConfigurationProperties("spring.cloud.tutu.discovery") public class TutuDiscoveryProperties { @Autowired private InetUtils inetUtils; private String serverAddr; private String service; private String ip; private int port = -1; private boolean secure = false; @PostConstruct public void init() throws Exception { if (!StringUtils.hasLength(ip)) { //获取服务IP地址 ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); } } //getters and setters } ``` TutuRegistration,图图服务注册实例: ```java public class TutuRegistration implements Registration { private TutuDiscoveryProperties tutuDiscoveryProperties; public TutuRegistration(TutuDiscoveryProperties tutuDiscoveryProperties) { this.tutuDiscoveryProperties = tutuDiscoveryProperties; } @Override public boolean isSecure() { return tutuDiscoveryProperties.isSecure(); } @Override public URI getUri() { return DefaultServiceInstance.getUri(this); } //getters and setters } ``` 注册和注销TutuRegistration的接口TutuServiceRegistry: ```java public class TutuServiceRegistry implements ServiceRegistry { private static final Logger logger = LoggerFactory.getLogger(TutuServiceRegistry.class); private TutuDiscoveryProperties tutuDiscoveryProperties; public TutuServiceRegistry(TutuDiscoveryProperties tutuDiscoveryProperties) { this.tutuDiscoveryProperties = tutuDiscoveryProperties; } /** * 注册服务实例 * * @param registration */ @Override public void register(Registration registration) { Map param = new HashMap<>(); param.put("serviceName", tutuDiscoveryProperties.getService()); param.put("ip", tutuDiscoveryProperties.getIp()); param.put("port", tutuDiscoveryProperties.getPort()); String result = HttpUtil.post(tutuDiscoveryProperties.getServerAddr() + "/register", param); if (Boolean.parseBoolean(result)) { logger.info("register service successfully, serviceName: {}, ip: {}, port: {}", tutuDiscoveryProperties.getService(), tutuDiscoveryProperties.getIp(), tutuDiscoveryProperties.getPort()); } else { logger.error("register service failed, serviceName: {}, ip: {}, port: {}", tutuDiscoveryProperties.getService(), tutuDiscoveryProperties.getIp(), tutuDiscoveryProperties.getPort()); throw new RuntimeException("register service failed, serviceName"); } } /** * 注销服务实例 * * @param registration */ @Override public void deregister(Registration registration) { Map param = new HashMap<>(); param.put("serviceName", tutuDiscoveryProperties.getService()); param.put("ip", tutuDiscoveryProperties.getIp()); param.put("port", tutuDiscoveryProperties.getPort()); String result = HttpUtil.post(tutuDiscoveryProperties.getServerAddr() + "/deregister", param); if (Boolean.parseBoolean(result)) { logger.info("de-register service successfully, serviceName: {}, ip: {}, port: {}", tutuDiscoveryProperties.getService(), tutuDiscoveryProperties.getIp(), tutuDiscoveryProperties.getPort()); } else { logger.warn("de-register service failed, serviceName: {}, ip: {}, port: {}", tutuDiscoveryProperties.getService(), tutuDiscoveryProperties.getIp(), tutuDiscoveryProperties.getPort()); } } } ``` AbstractAutoServiceRegistration实现类: ```java public class TutuAutoServiceRegistration extends AbstractAutoServiceRegistration { private TutuRegistration tutuRegistration; protected TutuAutoServiceRegistration(ServiceRegistry serviceRegistry, TutuRegistration tutuRegistration) { super(serviceRegistry, null); this.tutuRegistration = tutuRegistration; } @Override protected Registration getRegistration() { if (tutuRegistration.getPort() < 0) { //设置服务端口 tutuRegistration.setPort(this.getPort().get()); } return tutuRegistration; } } ``` 自动装配: TutuServiceRegistryAutoConfiguration: ```java /** * 自动配置服务注册相关类 */ @Configuration @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) public class TutuServiceRegistryAutoConfiguration { @Bean @ConditionalOnMissingBean public TutuDiscoveryProperties tutuProperties() { return new TutuDiscoveryProperties(); } @Bean public TutuRegistration tutuRegistration(TutuDiscoveryProperties tutuDiscoveryProperties) { return new TutuRegistration(tutuDiscoveryProperties); } @Bean public TutuServiceRegistry tutuServiceRegistry(TutuDiscoveryProperties tutuDiscoveryProperties) { return new TutuServiceRegistry(tutuDiscoveryProperties); } @Bean public TutuAutoServiceRegistration tutuAutoServiceRegistration(ServiceRegistry serviceRegistry, TutuRegistration tutuRegistration) { return new TutuAutoServiceRegistration(serviceRegistry, tutuRegistration); } } ``` META-INF/spring.factories: ```yaml org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.github.cloud.tutu.registry.TutuServiceRegistryAutoConfiguration ``` 测试: 1、maven install ![](./assets/service-registry-maven.png) 2、启动服务注册和发现中心TutuServerApplication 3、启动服务提供者ProviderApplication,其代码如下: ```java @RestController @SpringBootApplication public class ProviderApplication { @Value("${server.port}") private Integer port; public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } @PostMapping("/echo") public String echo() { return "Port of the service provider: " + port; } } ``` 配置application.yml: ```yaml spring: application: name: provider-application cloud: tutu: discovery: server-addr: localhost:6688 service: ${spring.application.name} # 随机端口 server: port: ${random.int[10000,20000]} ``` 4、浏览器中访问http://localhost:6688/list?serviceName=provider-application 或执行命令 ```curl -X GET 'http://localhost:6688/list?serviceName=provider-application'``` ,响应报文如下,说明服务已经注册到服务注册中心 ```json [ { "ip": "192.168.47.1", "port": 19588 } ] ```