上一篇文章 《Nacos是如何实现服务注册功能的》,我们全面解析了Nacos是如何实现服务注册的,那么这篇文章,我们就来手写一个Nacos,完成注册中心的服务注册功能。

源码在文末

为了更加贴近Nacos,我们本次总共开发三个模块。

  1. 模拟Nacos客户端【接入微服务】
  2. 模拟Nacos服务端
  3. 生产者微服务【为了测试】

手写Nacos客户端

对于这个模块的设计,主要有几下几点:

  1. 编写/META-INF/spring.factories文件加入自动装配类
  2. 将需要的类注入IOC容器
  3. 发布服务注册事件
  4. 监听发布的事件,准备向服务端发送数据

接下来,看具体实现

第一步,编写自动装配类

在这里插入图片描述

@Configuration(proxyBeanMethods = false)
@Import(InitRegistrySelect.class)
public class EnableRegistryConfiguration {

    /**
     * 远程调用
     * @return RestTemplate
     */
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

第二步,将我们需要的类注入IOC容器

public class InitRegistrySelect implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

						// 服务注册的事件监听
        return new String[]{"com.ossa.registry.core.OssaRegistry",
        		// 服务注册的事件发布
                "com.ossa.registry.core.OssaSmartLifecycle",
        // 客户端信息
        "com.ossa.registry.core.ClientInfo"};
    }
}

客户端信息


@ConfigurationProperties(prefix = "client.info")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClientInfo {
    private String ip;

    private String port;

    private String namespace;

    private String group;

    private String dataId;

    private String name;
}

事件

@Getter
@Setter
public class OssaEven extends ApplicationEvent {

    private ClientInfo clientInfo;

    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public OssaEven(Object source,ClientInfo clientInfo) {
        super(source);
        this.clientInfo = clientInfo;
    }
}

服务注册的事件发布

public class OssaSmartLifecycle implements SmartLifecycle {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private ClientInfo clientInfo;

    @Override
    public void start() {
    	// 发布事件
        applicationContext.publishEvent(new OssaEven(this,clientInfo));
    }

    @Override
    public void stop() {

    }

    @Override
    public boolean isRunning() {
        return false;
    }

    @Override
    public int getPhase() {
        return SmartLifecycle.super.getPhase();
    }
}

服务注册的事件监听

public class OssaRegistry {

    @Autowired
    private RestTemplate restTemplate;

    @EventListener
    public void registry(OssaEven even) {
        this.sendNacosClientInfo(even.getClientInfo());
    }

    private void sendNacosClientInfo(ClientInfo clientInfo) {

        HashMap<String, String> map = new HashMap<>();
        map.put("ip", clientInfo.getIp());
        map.put("port", clientInfo.getPort());
        map.put("name", clientInfo.getName());

        ResponseEntity<String> entity = restTemplate.postForEntity(
                "http://" + clientInfo.getIp() + ":" + clientInfo.getPort() + "/ossa/registry",
                map,
                String.class
        );

        System.out.println(entity.getBody());
    }
}

生产者微服务

对于这个模块的设计而言,很简单

  1. 引入手写的Nacos客户端模块/依赖
  2. 将手写的Nacos客户端模块中所需要的相关信息配置在配置文件中
    首先在生产者服务引入【手写Nacos客户端模块】
<dependency>
    <groupId>com.ossa</groupId>
    <artifactId>ossa-spring-cloud-registry</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

编写application.yml文件,配置nacos信息

client:
  info:
    ip: 127.0.0.1
    port: 8888
    name: service-producer

手写Nacos服务端

这个设计就更简单了:

  1. 接受注册信息的API接口
  2. 保存到内存中
  3. 查询接口,测试是否成功

API接口

@RestController
@RequestMapping("/ossa/registry")
public class RegistryRest {

    @Autowired
    private RegistryService registryService;

    @PostMapping
    public String registry(@RequestBody ClientInfo clientInfo) {

        return registryService.registry(clientInfo);
    }

    @GetMapping
    public ResponseEntity<Map<String, Map<String, Instance>>> getAll() {
       return ResponseEntity.ok(registryService.getAll());
    }
}

注册信息存储

public class ServiceManager {

    /**
     * Map(namespace, Map(group::serviceName, Instance)).
     */
    public final static Map<String, Map<String, Instance>> serviceMap = new ConcurrentHashMap<>();

}

具体实现

@Service
public class RegistryServiceImpl implements RegistryService {
    @Override
    public String registry(ClientInfo clientInfo) {
        String ns = clientInfo.getNamespace();
        String group = clientInfo.getGroup();
        String name = clientInfo.getName();
        Instance instance = new Instance();
        instance.setInfo(clientInfo.getIp() + ":" + clientInfo.getPort());
        HashMap<String, Instance> map = new HashMap<>();
        map.put(group == null ? "DEFAULT_GROUP:" + name : group + ":" + name, instance);
        ServiceManager.serviceMap.put(ns == null ? "DEFAULT_NS" : ns, map);
        return "ok";
    }

    @Override
    public Map<String, Map<String, Instance>> getAll() {
        return ServiceManager.serviceMap;
    }
}

测试

服务启动成功,并返回“OK”信息,说明数据发送成功。

服务启动成功
获取服务
在这里插入图片描述
举一反三,在此基础上,我们想做什么都可以了。

那么,我们接下里,解析一下Nacos的心跳机制并手写一个Nacos的心跳机制,让大家更好的了解Nacos。奥利给!!!!

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐