Naocs2.x】(六)在乞丐版注册中心写乞丐--实现篇
前言
上一节设计了一个丐中丐版本的注册中心。这一节顺理成章,该去实现它了。
代码仓库在文章结尾处。
Mung
这一块主要是,注册中心服务端的逻辑。
服务实体
主要是客户端名称、IP、端口、最后一次通信时间等等。
@Data
public class Client {
private String clientId;
private String clientName;
private String clientIp;
private Integer port;
private Long lastTime;
private LocalDateTime registerTime;
}
业务逻辑
目前客户端信息,就保存在内存中。丐版先不进行持久化了。
实现类主要就是针对 client_Map
的增删改查。
业务接口
public interface ClientService {
/** 客户端Map. key:clientName value:Client */
Map<String, Client> client_Map = new ConcurrentHashMap<>(16);
/**
* 添加客户端
* @apiNote [附加描述]
* @param client 客户端信息
* @author DaMai
* @date 2021/9/7 20:53
*/
void addClient(Client client);
/**
* 删除客户端
* @apiNote [附加描述]
* @param clientName 客户端名称
* @author DaMai
* @date 2021/9/7 20:53
*/
void delClient(String clientName);
/**
* 心跳
* @apiNote [附加描述]
* @param clientName 客户端名称
* @author DaMai
* @date 2021/9/8 12:27
*/
void beat(String clientName);
/**
* 服务端触发客户端主动心跳请求
* @apiNote 一般用于服务端要剔除客户端时,进行客户端探测。客户端接受到此请求时,立即发送心跳。
* @param clientName 客户端名称
* @return com.ruiruan.pre.common.core.vo.ResultVO<java.lang.Void>
* @author DaMai
* @date 2021/9/12 14:08
*/
ResultVO<Void> initiativeHealthCheck(String clientName);
/**
* 获取客户端列表
* @apiNote [附加描述]
* @return java.util.List<pri.damai.model.Client>
* @author DaMai
* @date 2021/9/8 12:38
*/
List<Client> getClientList();
/**
* 获取客户端
* @apiNote [附加描述]
* @param clientName 客户端名称
* @return pri.damai.model.Client
* @author DaMai
* @date 2021/9/7 21:06
*/
Client getClientByClientName(String clientName);
业务实现
这里主要就拿出来 客户端过期 的逻辑。
@Slf4j
@Service
public class TempServiceImpl implements ClientService {
……………………………………………………………………
@PostConstruct
public void init() {
GlobalExecutor.MUNG_SERVER_CLIENT_CLEAR.scheduleAtFixedRate(new ExpiredClientCleaner(), 6, 5, TimeUnit.SECONDS);
}
public static class ExpiredClientCleaner implements Runnable {
@Override
public void run() {
long millis = System.currentTimeMillis();
for (String key : client_Map.keySet()) {
Client client = client_Map.get(key);
if (Objects.nonNull(client) && client.ifExpired(millis)) {
client_Map.remove(key);
log.debug("移除客户端:{}", client);
}
}
}
}
}
HTTP 接口层
就是暴露业务的HTTP 接口,没有其他复杂操作。
@RuiRuanLog
@RestController
@RequestMapping(ClientConst.BASE_URL)
public class ClientController {
@Resource
ClientService clientService;
@PostMapping(ClientConst.ADD_URL)
public ResultVO<Void> addClient(@RequestBody Client client) {
clientService.addClient(client);
return ResultVO.success();
}
@GetMapping(ClientConst.DEL_CLIENT_URL)
public ResultVO<Void> delClient(String clientName) {
clientService.delClient(clientName);
return ResultVO.success();
}
@GetMapping(ClientConst.CLIENT_LIST_URL)
public ResultVO<List<Client>> getClientList() {
return ResultVO.success(clientService.getClientList());
}
@GetMapping(ClientConst.GET_SERVICE_LIST_URL)
public ResultVO<List<String>> getServiceIds() {
List<String> result = clientService.getClientList().stream().map(Client::getClientId).collect(Collectors.toList());
return ResultVO.success(result);
}
@GetMapping(ClientConst.GET_CLIENT_URL)
public ResultVO<Client> getClient(String clientName) {
return ResultVO.success(clientService.getClientByClientName(clientName));
}
@GetMapping(ClientConst.BEAT)
public ResultVO<Void> beat(String clientName) {
clientService.beat(clientName);
return ResultVO.success();
}
@GetMapping(ClientConst.INITIATIVE_HEALTH_CHECK)
public ResultVO<Void> initiativeHealthCheck(String clientName) {
clientService.initiativeHealthCheck(clientName);
return ResultVO.success();
}
}
Mung-Client
这里主要就是对 HTTP 接口层做一层包装,方便使用者进行调用。
不再粘贴代码了,有兴趣的话,看一眼代码。
Mung-Starter
这里就需要将 Mung-Client 与 Spring Cloud 结合起来了,所以 POM 里应该依赖 Mung-Client
与spring-cloud-commons
。
还需要进行 Feign 调用,所以需要依赖 Ribbon
。
其他的 Spring Boot 项目基础依赖就不赘述了,有兴趣看代码就行。
服务注册
Registration 实现
public class MungRegistration implements Registration {
private String clientName;
private String clientIp;
private Integer port;
public MungRegistration(String clientName, String clientIp, Integer port) {
this.clientName = clientName;
this.clientIp = clientIp;
this.port = port;
}
@Override
public String getServiceId() {return this.clientName;}
@Override
public String getHost() {return this.clientIp;}
@Override
public int getPort() {return this.port;}
@Override
public boolean isSecure() {return false;}
@Override
public URI getUri() {return null;}
@Override
public Map<String, String> getMetadata() {return null;}
}
ServiceRegistry 实现
这里的重点方法两个:注册、注销。
public class MungServiceRegistry implements ServiceRegistry<MungRegistration> {
private final String clientName;
private final String clientIp;
private final Integer clientPort;
private final MungClient mungClient;
public MungServiceRegistry(String serverIp, Integer serverPort, String clientName, String clientIp, Integer clientPort) {
this.clientName = clientName;
this.clientIp = clientIp;
this.clientPort = clientPort;
this.mungClient = new MungClient(serverIp, serverPort);
}
@Override
public void register(MungRegistration registration) {
if (Objects.isNull(registration)) {
return;
}
Client client = new Client();
client.setClientId(clientName);
client.setClientName(clientName);
client.setClientIp(clientIp);
client.setPort(clientPort);
ResultVO<Void> resultVO = mungClient.addClient(client);
this.checkSuccess(resultVO);
}
@Override
public void deregister(MungRegistration registration) {
if (Objects.isNull(registration)) {
return;
}
ResultVO<Void> resultVO = mungClient.delClient(registration.getInstanceId());
this.checkSuccess(resultVO);
}
}
AbstractAutoServiceRegistration 实现
这里的重点方法一个:getRegistration()
。
public class MungAutoServiceRegistration extends AbstractAutoServiceRegistration<MungRegistration> {
private MungRegistration registration;
@Override
protected void register() {
super.register();
}
@Override
protected boolean isEnabled() {
return true;
}
@Override
protected MungRegistration getRegistration() {
return this.registration;
}
}
自动配置类
最后就是 Spring Boot 的自动配置类了。
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
matchIfMissing = true)
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class,
MungProperties.class,
})
public class MungRegistryAutoConfiguration {
@Bean
public MungServiceRegistry mungServiceRegistry(MungProperties mungProperties) {
String[] split = mungProperties.getServerAddress().split(":");
return new MungServiceRegistry(split[0], Integer.parseInt(split[1]), mungProperties.getClientName(), mungProperties.getBusIp(), mungProperties.getBusPort());
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public MungRegistration mungRegistration(MungProperties mungProperties) {
return new MungRegistration(mungProperties.getClientName(), mungProperties.getBusIp(), mungProperties.getBusPort());
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public MungAutoServiceRegistration mungAutoServiceRegistration(
MungServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
MungRegistration registration) {
return new MungAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
}
}
Ribbon
Server 实现
这里的 Server 主要是对远程服务信息的抽象。
public class MungServer extends Server {
private final MetaInfo metaInfo;
private final Client client;
private final Map<String, String> metadata;
public MungServer(final Client client) {
super(client.getClientIp(), client.getPort());
this.client = client;
this.metaInfo = new Server.MetaInfo() {
@Override
public String getAppName() { return client.getClientName(); }
@Override
public String getServerGroup() { return null; }
@Override
public String getServiceIdForDiscovery() { return null; }
@Override
public String getInstanceId() { return client.getClientId(); }
};
this.metadata = null;
}
@Override
public Server.MetaInfo getMetaInfo() { return metaInfo; }
public Client getInstance() { return client; }
public Map<String, String> getMetadata() { return metadata; }
}
AbstractServerList 实现
Ribbon 内部会定时调用getUpdatedListOfServers()
方法,来获取远程服务元信息。
public class MungServerList extends AbstractServerList<MungServer> {
private MungClient mungClient;
private String clientId;
public MungServerList(MungProperties mungProperties, String clientId) {
this.clientId = clientId;
this.mungClient = new MungClient(mungProperties.getServerIp(), mungProperties.getServerPort());
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {this.clientId = iClientConfig.getClientName();}
@Override
public List<MungServer> getInitialListOfServers() {return getServerList();}
@Override
public List<MungServer> getUpdatedListOfServers() {return getServerList();}
private List<MungServer> getServerList() {
ResultVO<Client> resultVO = mungClient.getClient(clientId);
MungServer mungServer = new MungServer(resultVO.getData());
return Collections.singletonList(mungServer);
}
}
其余的就是 Spring Boot 的配置类。详细可以看代码。
业务服务一
- POM 文件依赖
mung-start
。 - yml配置如下:
server:
port: 8801
spring:
application:
name: bus-one
damai:
mung:
discovery:
bus-ip: 127.0.0.1
server-address: 127.0.0.1:8889
-
写一个业务接口
@RestController @RequestMapping("/busOne") public class HelloController { @GetMapping("/hi") public String hi() { return "hi, this is bus-one 8801";} }
业务服务二
-
前面两步如业务服务一。
-
写个 Feign 调用
@FeignClient(name = "bus-one") public interface BusOneFeign { @GetMapping("/busOne/hi") String hi(); }
-
Feign 调用
@RestController @RequestMapping("/two") public class TwoController { @Resource BusOneFeign busOneFeign; @GetMapping("/hi") public String hi() { return busOneFeign.hi(); } }
-
服务调用
这里可以看到,我们成功使用 Feign 进行了远程接口调用。
小节
这一节把上节的设计,做了一个简单实现。目前可以做到简单的调用了。
后续可以把注册的服务修改为集群(List),然后继承 Ribbon 的 IRule
类,通过choose()
方法,来进行负载均衡等功能。
Mung 项目代码地址