commit b4e6ccd888898b1ac5863050aa72dce31c3ec3bd Author: DerekYRC <15521077528@163.com> Date: Fri Apr 1 20:20:16 2022 +0800 服务注册 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39a4a81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +out/ +target/ +.idea/ +.idea_modules/ +*.iml +*.ipr +*.iws \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ee9f61 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# mini-spring-cloud +[![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/DerekYRC/mini-spring-cloud) +[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) +[![Stars](https://img.shields.io/github/stars/DerekYRC/mini-spring-cloud)](https://img.shields.io/github/stars/DerekYRC/mini-spring-cloud) +[![Forks](https://img.shields.io/github/forks/DerekYRC/mini-spring-cloud)](https://img.shields.io/github/forks/DerekYRC/mini-spring-cloud) + +**姊妹版:**[**mini-spring**](https://github.com/DerekYRC/mini-spring/blob/main/README_CN.md) **(简化版的spring框架)** + +## 关于 +**mini-spring-cloud**是简化版的spring-cloud框架,能帮助你快速熟悉spring-cloud源码及掌握其核心原理。在保留spring cloud核心功能的的前提下尽量精简代码,核心功能包括服务注册、服务发现、负载均衡、集成Feign简化调用、流量控制、熔断降级、API网关等。 + +希望本项目对你有所帮助,请给个**STAR吧,谢谢!!!** + +## 功能 +#### 基础篇 +* [服务注册](https://github.com/DerekYRC/mini-spring-cloud/blob/main/changelog.md#服务注册) +* [服务发现](https://github.com/DerekYRC/mini-spring-cloud/blob/main/changelog.md#服务发现) +* [负载均衡](https://github.com/DerekYRC/mini-spring-cloud/blob/main/changelog.md#集成ribbon实现客户端负载均衡) +* [集成Feign简化调用]() +* [流量控制]() +* [熔断降级]() +* [API 网关]() + +#### 扩展篇 +> TODO 基础篇遗留的功能点 + +* [自定义负载均衡规则]() + + + +## 使用方法 +阅读 [changelog.md](https://github.com/DerekYRC/mini-spring-cloud/blob/main/changelog.md) + +## 常见问题 +[**点此提问**](https://github.com/DerekYRC/mini-spring-cloud/issues/1) + +常见问题列表: [faq.md](https://github.com/DerekYRC/mini-spring-cloud/blob/main/faq.md) + +## 贡献 +欢迎Pull Request + +## 联系我 +手机/微信:**15521077528** + +邮箱:**15521077528@163.com** + +## 参考 +- [《精尽 Spring Cloud 学习指南》](http://svip.iocoder.cn/Spring-Cloud/tutorials/) + +## 版权说明 + +本项目可用于个人学习、非商业性或非盈利性用途。将本项目用于其他用途时,须征得本人的书面许可。 \ No newline at end of file diff --git a/assets/service-registry-api.png b/assets/service-registry-api.png new file mode 100644 index 0000000..c09f1eb Binary files /dev/null and b/assets/service-registry-api.png differ diff --git a/assets/service-registry-maven.png b/assets/service-registry-maven.png new file mode 100644 index 0000000..356038e Binary files /dev/null and b/assets/service-registry-maven.png differ diff --git a/assets/spring-cloud.png b/assets/spring-cloud.png new file mode 100644 index 0000000..a2595e5 Binary files /dev/null and b/assets/spring-cloud.png differ diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..13db07f --- /dev/null +++ b/changelog.md @@ -0,0 +1,338 @@ +# [前言](#前言) + +前置知识。阅读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 + } +] +``` + diff --git a/faq.md b/faq.md new file mode 100644 index 0000000..187aa20 --- /dev/null +++ b/faq.md @@ -0,0 +1 @@ +# 常见问题列表 \ No newline at end of file diff --git a/mini-spring-cloud-examples/mini-spring-cloud-provider-example/pom.xml b/mini-spring-cloud-examples/mini-spring-cloud-provider-example/pom.xml new file mode 100644 index 0000000..7f0cb4f --- /dev/null +++ b/mini-spring-cloud-examples/mini-spring-cloud-provider-example/pom.xml @@ -0,0 +1,36 @@ + + + + mini-spring-cloud + com.github + 1.0.0-SNAPSHOT + ../../pom.xml + + 4.0.0 + + mini-spring-cloud-provider-example + + + + org.springframework.boot + spring-boot-starter-web + + + + com.github + mini-spring-cloud-tutu-discovery + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/mini-spring-cloud-examples/mini-spring-cloud-provider-example/src/main/java/com/github/cloud/examples/ProviderApplication.java b/mini-spring-cloud-examples/mini-spring-cloud-provider-example/src/main/java/com/github/cloud/examples/ProviderApplication.java new file mode 100644 index 0000000..e478063 --- /dev/null +++ b/mini-spring-cloud-examples/mini-spring-cloud-provider-example/src/main/java/com/github/cloud/examples/ProviderApplication.java @@ -0,0 +1,28 @@ +package com.github.cloud.examples; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author derek(易仁川) + * @date 2022/3/19 + */ +@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; + } +} diff --git a/mini-spring-cloud-examples/mini-spring-cloud-provider-example/src/main/resources/application.yml b/mini-spring-cloud-examples/mini-spring-cloud-provider-example/src/main/resources/application.yml new file mode 100644 index 0000000..e707537 --- /dev/null +++ b/mini-spring-cloud-examples/mini-spring-cloud-provider-example/src/main/resources/application.yml @@ -0,0 +1,12 @@ +spring: + application: + name: provider-application + cloud: + tutu: + discovery: + server-addr: localhost:6688 + service: ${spring.application.name} + +# 随机端口 +server: + port: ${random.int[10000,20000]} \ No newline at end of file diff --git a/mini-spring-cloud-examples/tutu-server/pom.xml b/mini-spring-cloud-examples/tutu-server/pom.xml new file mode 100644 index 0000000..6f4391f --- /dev/null +++ b/mini-spring-cloud-examples/tutu-server/pom.xml @@ -0,0 +1,41 @@ + + + + mini-spring-cloud + com.github + 1.0.0-SNAPSHOT + ../../pom.xml + + 4.0.0 + + tutu-server + + + + org.springframework.boot + spring-boot-starter-web + + + + com.alibaba + fastjson + + + + cn.hutool + hutool-all + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/mini-spring-cloud-examples/tutu-server/src/main/java/com/github/cloud/examples/TutuServerApplication.java b/mini-spring-cloud-examples/tutu-server/src/main/java/com/github/cloud/examples/TutuServerApplication.java new file mode 100644 index 0000000..18ab042 --- /dev/null +++ b/mini-spring-cloud-examples/tutu-server/src/main/java/com/github/cloud/examples/TutuServerApplication.java @@ -0,0 +1,133 @@ +package com.github.cloud.examples; + +import com.alibaba.fastjson.JSON; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author derek(易仁川) + * @date 2022/3/19 + */ + +@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; + + public Server(String ip, Integer port) { + this.ip = ip; + this.port = port; + } + + public String getIp() { + return ip; + } + + public Integer getPort() { + return port; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Server server = (Server) o; + + if (!ip.equals(server.ip)) return false; + return port.equals(server.port); + } + + @Override + public int hashCode() { + int result = ip.hashCode(); + result = 31 * result + port.hashCode(); + return result; + } + } +} diff --git a/mini-spring-cloud-examples/tutu-server/src/main/resources/application.yml b/mini-spring-cloud-examples/tutu-server/src/main/resources/application.yml new file mode 100644 index 0000000..dca48fa --- /dev/null +++ b/mini-spring-cloud-examples/tutu-server/src/main/resources/application.yml @@ -0,0 +1,2 @@ +server: + port: 6688 \ No newline at end of file diff --git a/mini-spring-cloud-tutu-discovery/pom.xml b/mini-spring-cloud-tutu-discovery/pom.xml new file mode 100644 index 0000000..632063a --- /dev/null +++ b/mini-spring-cloud-tutu-discovery/pom.xml @@ -0,0 +1,67 @@ + + + + mini-spring-cloud + com.github + 1.0.0-SNAPSHOT + + 4.0.0 + + mini-spring-cloud-tutu-discovery + 1.0.0-SNAPSHOT + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot + true + + + + org.springframework.boot + spring-boot-autoconfigure + true + + + + org.springframework.boot + spring-boot-starter + true + + + + org.springframework.cloud + spring-cloud-commons + + + + org.springframework.cloud + spring-cloud-context + + + + org.springframework.boot + spring-boot-starter-web + test + + + + cn.hutool + hutool-all + + + + com.alibaba + fastjson + + + + \ No newline at end of file diff --git a/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/TutuDiscoveryProperties.java b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/TutuDiscoveryProperties.java new file mode 100644 index 0000000..e203503 --- /dev/null +++ b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/TutuDiscoveryProperties.java @@ -0,0 +1,77 @@ +package com.github.cloud.tutu; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.commons.util.InetUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; + +/** + * @author derek(易仁川) + * @date 2022/3/19 + */ +@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(); + } + } + + public String getServerAddr() { + return serverAddr; + } + + public void setServerAddr(String serverAddr) { + this.serverAddr = serverAddr; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isSecure() { + return secure; + } + + public void setSecure(boolean secure) { + this.secure = secure; + } +} diff --git a/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuAutoServiceRegistration.java b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuAutoServiceRegistration.java new file mode 100644 index 0000000..7480fc1 --- /dev/null +++ b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuAutoServiceRegistration.java @@ -0,0 +1,45 @@ +package com.github.cloud.tutu.registry; + +import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; + +/** + * 服务自动注册 + * + * @author derek(易仁川) + * @date 2022/3/19 + */ +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; + } + + @Override + protected Object getConfiguration() { + return tutuRegistration.getTutuDiscoveryProperties(); + } + + @Override + protected boolean isEnabled() { + return true; + } + + @Override + protected Registration getManagementRegistration() { + return null; + } +} diff --git a/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuRegistration.java b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuRegistration.java new file mode 100644 index 0000000..5e44fe9 --- /dev/null +++ b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuRegistration.java @@ -0,0 +1,61 @@ +package com.github.cloud.tutu.registry; + +import com.github.cloud.tutu.TutuDiscoveryProperties; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.serviceregistry.Registration; + +import java.net.URI; +import java.util.Map; + +/** + * tutu服务 + * + * @author derek(易仁川) + * @date 2022/3/19 + */ +public class TutuRegistration implements Registration { + + private TutuDiscoveryProperties tutuDiscoveryProperties; + + public TutuRegistration(TutuDiscoveryProperties tutuDiscoveryProperties) { + this.tutuDiscoveryProperties = tutuDiscoveryProperties; + } + + @Override + public String getServiceId() { + return tutuDiscoveryProperties.getService(); + } + + @Override + public String getHost() { + return tutuDiscoveryProperties.getIp(); + } + + @Override + public int getPort() { + return tutuDiscoveryProperties.getPort(); + } + + public void setPort(int port) { + this.tutuDiscoveryProperties.setPort(port); + } + + @Override + public boolean isSecure() { + return tutuDiscoveryProperties.isSecure(); + } + + @Override + public URI getUri() { + return DefaultServiceInstance.getUri(this); + } + + @Override + public Map getMetadata() { + return null; + } + + public TutuDiscoveryProperties getTutuDiscoveryProperties() { + return tutuDiscoveryProperties; + } +} diff --git a/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuServiceRegistry.java b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuServiceRegistry.java new file mode 100644 index 0000000..72f97c2 --- /dev/null +++ b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuServiceRegistry.java @@ -0,0 +1,87 @@ +package com.github.cloud.tutu.registry; + +import cn.hutool.http.HttpUtil; +import com.github.cloud.tutu.TutuDiscoveryProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; + +import java.util.HashMap; +import java.util.Map; + +/** + * 具体的服务注册实现类 + * + * @author derek(易仁川) + * @date 2022/3/19 + */ +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()); + } + } + + @Override + public void close() { + + } + + @Override + public void setStatus(Registration registration, String status) { + throw new UnsupportedOperationException(); + } + + @Override + public T getStatus(Registration registration) { + return null; + } +} diff --git a/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuServiceRegistryAutoConfiguration.java b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuServiceRegistryAutoConfiguration.java new file mode 100644 index 0000000..68f678e --- /dev/null +++ b/mini-spring-cloud-tutu-discovery/src/main/java/com/github/cloud/tutu/registry/TutuServiceRegistryAutoConfiguration.java @@ -0,0 +1,41 @@ +package com.github.cloud.tutu.registry; + +import com.github.cloud.tutu.TutuDiscoveryProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 自动配置服务注册相关类 + * + * @author derek(易仁川) + * @date 2022/3/19 + */ +@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); + } +} diff --git a/mini-spring-cloud-tutu-discovery/src/main/resources/META-INF/spring.factories b/mini-spring-cloud-tutu-discovery/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..50f8c9d --- /dev/null +++ b/mini-spring-cloud-tutu-discovery/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.github.cloud.tutu.registry.TutuServiceRegistryAutoConfiguration \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a73dbd2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + + org.springframework.cloud + spring-cloud-build + 3.1.1 + + + + com.github + mini-spring-cloud + pom + 1.0.0-SNAPSHOT + + + mini-spring-cloud-examples/tutu-server + mini-spring-cloud-tutu-discovery + mini-spring-cloud-examples/mini-spring-cloud-provider-example + + + + 2021.0.1 + 1.2.79 + 5.7.21 + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + + + + com.github + mini-spring-cloud-tutu-discovery + 1.0.0-SNAPSHOT + + + + com.alibaba + fastjson + ${fastjson.version} + + + + cn.hutool + hutool-all + ${hutool.version} + + + + + \ No newline at end of file