服务发现

service-discovery
DerekYRC 2 years ago
parent b4e6ccd888
commit a7ec79e936

@ -4,13 +4,13 @@
- 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 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的源码能起到事半功倍的效果所以本项目的功能实现逻辑及原理和官方保持一致但追求代码最大精简化。
写作本项目的目的之一是降低阅读原始spring cloud源码的难度。希望掌握本项目讲解的内容之后再阅读原始spring-cloud的源码能起到事半功倍的效果所以本项目的功能实现逻辑及原理和官方保持一致但追求代码最大精简化,可以理解为一个源码导读的项目
技术能力有限且文采不佳,大家可以在此[**issue**](https://github.com/DerekYRC/mini-spring-cloud/issues/1) 留言提问题和发表建议也欢迎Pull Request完善此项目。
@ -335,4 +335,176 @@ server:
}
]
```
# [服务发现](#服务发现)
> 分支: service-discovery
spring-cloud-commons定义的服务发现接口```org.springframework.cloud.client.discovery.DiscoveryClient```:
```java
public interface DiscoveryClient extends Ordered {
/**
* Gets all ServiceInstances associated with a particular serviceId.
* @param serviceId The serviceId to query.
* @return A List of ServiceInstance.
*/
List<ServiceInstance> getInstances(String serviceId);
/**
* @return All known service IDs.
*/
List<String> getServices();
}
```
仅需实现DiscoveryClient接口即可实现类:
```java
/**
* 服务发现实现类
*/
public class TutuDiscoveryClient implements DiscoveryClient {
private static final Logger logger = LoggerFactory.getLogger(TutuDiscoveryClient.class);
private TutuDiscoveryProperties tutuDiscoveryProperties;
public TutuDiscoveryClient(TutuDiscoveryProperties tutuDiscoveryProperties) {
this.tutuDiscoveryProperties = tutuDiscoveryProperties;
}
@Override
public List<ServiceInstance> getInstances(String serviceId) {
Map<String, Object> param = new HashMap<>();
param.put("serviceName", serviceId);
String response = HttpUtil.get(tutuDiscoveryProperties.getServerAddr() + "/list", param);
logger.info("query service instance, serviceId: {}, response: {}", serviceId, response);
return JSON.parseArray(response).stream().map(hostInfo -> {
TutuServiceInstance serviceInstance = new TutuServiceInstance();
serviceInstance.setServiceId(serviceId);
String ip = ((JSONObject) hostInfo).getString("ip");
Integer port = ((JSONObject) hostInfo).getInteger("port");
serviceInstance.setHost(ip);
serviceInstance.setPort(port);
return serviceInstance;
}).collect(Collectors.toList());
}
@Override
public List<String> getServices() {
String response = HttpUtil.post(tutuDiscoveryProperties.getServerAddr() + "/listServiceNames", new HashMap<>());
logger.info("query service instance list, response: {}", response);
return JSON.parseArray(response, String.class);
}
}
```
自动装配TutuDiscoveryAutoConfiguration:
```java
@Configuration
public class TutuDiscoveryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TutuDiscoveryProperties tutuDiscoveryProperties() {
return new TutuDiscoveryProperties();
}
@Bean
public DiscoveryClient tutuDiscoveryClient(TutuDiscoveryProperties tutuDiscoveryProperties) {
return new TutuDiscoveryClient(tutuDiscoveryProperties);
}
}
```
spring.factories:
```yaml
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.github.cloud.tutu.registry.TutuServiceRegistryAutoConfiguration,\
com.github.cloud.tutu.discovery.TutuDiscoveryAutoConfiguration
```
测试:
1、maven install启动服务注册和发现中心TutuServerApplication启动服务提供者ProviderApplication启动服务消费者ConsumerApplication(后续测试步骤均同此,不再提及)
服务消费者代码如下:
```java
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@RestController
static class HelloController {
@Autowired
private DiscoveryClient discoveryClient;
private RestTemplate restTemplate = new RestTemplate();
@GetMapping("/hello")
public String hello() {
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("provider-application");
if (serviceInstances.size() > 0) {
ServiceInstance serviceInstance = serviceInstances.get(0);
URI uri = serviceInstance.getUri();
String response = restTemplate.postForObject(uri.toString() + "/echo", null, String.class);
return response;
}
throw new RuntimeException("No service instance for provider-application found");
}
}
}
```
application.yml:
```yaml
spring:
application:
name: consumer-application
cloud:
tutu:
discovery:
server-addr: localhost:6688
service: ${spring.application.name}
```
2、访问http://localhost:8080/hello ,相应报文如下:
```yaml
Port of the service provider: 19922
```

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mini-spring-cloud</artifactId>
<groupId>com.github</groupId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mini-spring-cloud-consumer-examples</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github</groupId>
<artifactId>mini-spring-cloud-tutu-discovery</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,48 @@
package com.github.cloud.examples;
import com.github.cloud.tutu.discovery.TutuDiscoveryClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.List;
/**
* @author derek()
* @date 2022/3/20
*/
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@RestController
static class HelloController {
@Autowired
private TutuDiscoveryClient discoveryClient;
private RestTemplate restTemplate = new RestTemplate();
@GetMapping("/hello")
public String hello() {
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("provider-application");
if (serviceInstances.size() > 0) {
ServiceInstance serviceInstance = serviceInstances.get(0);
URI uri = serviceInstance.getUri();
String response = restTemplate.postForObject(uri.toString() + "/echo", null, String.class);
return response;
}
throw new RuntimeException("No service instance for provider-application found");
}
}
}

@ -0,0 +1,8 @@
spring:
application:
name: consumer-application
cloud:
tutu:
discovery:
server-addr: localhost:6688
service: ${spring.application.name}

@ -0,0 +1,85 @@
package com.github.cloud.tutu;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import java.net.URI;
import java.util.Map;
/**
*
*
* @author derek()
* @date 2022/3/20
*/
public class TutuServiceInstance implements ServiceInstance {
private String serviceId;
private String host;
private int port;
private boolean secure = false;
private Map<String, String> metadata;
public TutuServiceInstance() {
}
public TutuServiceInstance(String serviceId, String host, int port) {
this.serviceId = serviceId;
this.host = host;
this.port = port;
}
@Override
public String getServiceId() {
return serviceId;
}
@Override
public String getHost() {
return host;
}
@Override
public int getPort() {
return port;
}
@Override
public boolean isSecure() {
return secure;
}
@Override
public URI getUri() {
return DefaultServiceInstance.getUri(this);
}
@Override
public Map<String, String> getMetadata() {
return metadata;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
public void setSecure(boolean secure) {
this.secure = secure;
}
public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}
}

@ -0,0 +1,25 @@
package com.github.cloud.tutu.discovery;
import com.github.cloud.tutu.TutuDiscoveryProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author derek()
* @date 2022/3/20
*/
@Configuration
public class TutuDiscoveryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TutuDiscoveryProperties tutuDiscoveryProperties() {
return new TutuDiscoveryProperties();
}
@Bean
public TutuDiscoveryClient tutuDiscoveryClient(TutuDiscoveryProperties tutuDiscoveryProperties) {
return new TutuDiscoveryClient(tutuDiscoveryProperties);
}
}

@ -0,0 +1,62 @@
package com.github.cloud.tutu.discovery;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.cloud.tutu.TutuDiscoveryProperties;
import com.github.cloud.tutu.TutuServiceInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
*
*
* @author derek()
* @date 2022/3/20
*/
public class TutuDiscoveryClient implements DiscoveryClient {
private static final Logger logger = LoggerFactory.getLogger(TutuDiscoveryClient.class);
private TutuDiscoveryProperties tutuDiscoveryProperties;
public TutuDiscoveryClient(TutuDiscoveryProperties tutuDiscoveryProperties) {
this.tutuDiscoveryProperties = tutuDiscoveryProperties;
}
@Override
public List<ServiceInstance> getInstances(String serviceId) {
Map<String, Object> param = new HashMap<>();
param.put("serviceName", serviceId);
String response = HttpUtil.get(tutuDiscoveryProperties.getServerAddr() + "/list", param);
logger.info("query service instance, serviceId: {}, response: {}", serviceId, response);
return JSON.parseArray(response).stream().map(hostInfo -> {
TutuServiceInstance serviceInstance = new TutuServiceInstance();
serviceInstance.setServiceId(serviceId);
String ip = ((JSONObject) hostInfo).getString("ip");
Integer port = ((JSONObject) hostInfo).getInteger("port");
serviceInstance.setHost(ip);
serviceInstance.setPort(port);
return serviceInstance;
}).collect(Collectors.toList());
}
@Override
public List<String> getServices() {
String response = HttpUtil.post(tutuDiscoveryProperties.getServerAddr() + "/listServiceNames", new HashMap<>());
logger.info("query service instance list, response: {}", response);
return JSON.parseArray(response, String.class);
}
@Override
public String description() {
return "Spring Cloud Tutu Discovery Client";
}
}

@ -1,2 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.github.cloud.tutu.registry.TutuServiceRegistryAutoConfiguration
com.github.cloud.tutu.registry.TutuServiceRegistryAutoConfiguration,\
com.github.cloud.tutu.discovery.TutuDiscoveryAutoConfiguration

@ -20,6 +20,7 @@
<module>mini-spring-cloud-examples/tutu-server</module>
<module>mini-spring-cloud-tutu-discovery</module>
<module>mini-spring-cloud-examples/mini-spring-cloud-provider-example</module>
<module>mini-spring-cloud-examples/mini-spring-cloud-consumer-examples</module>
</modules>
<properties>

Loading…
Cancel
Save