ServiceComb使用Consul开源开发任务总结
【摘要】 Apache ServiceComb Java Chassis 给开发者提供一个快速构建微服务的JAVA SDK,本文讲解了使用consul作为注册中心、配置中心的实现过程,同时也讲解了提交PR的过程
简介
Apache ServiceComb Java Chassis 给开发者提供一个快速构建微服务的JAVA SDK。它包含如下特性:
- 基于Open API的契约优先(API First)开发模式,满足开发过程可管理、开发规范可验证要求。
- 多种开发风格,REST(JAX-RS、Spring MVC)和RPC等,高效支持遗留系统迁移和新系统开发场景。
- 多种通信协议, HTTP over Vert.x、Http Over Servlet、Highway等,满足不同场景对于性能、韧性的需求。
- 统一一致的服务提供者、服务消费者处理链,易于扩展新功能。
提供服务发现、配置管理、熔断、限流、灰度发布等开箱即用的服务治理能力。
在github上Fork servicecomb-java-chassis 代码,并下载代码
consul服务注册、发现实现
- 创建registry-consul模块:
- 在pom中添加相关依赖,与consul有关的依赖如下:
<dependency> <groupId>org.kiwiproject</groupId> <artifactId>consul-client</artifactId> <version>1.4.2</version> </dependency>
- 实现注册功能
1)构建ConsulRegistrationInstance实例:consulInstance
2)利用consulInstance的信息构建NewService的实例newServie
3)使用consul-api提供的方法注册newServie
示例代码如下:@Override public ConsulRegistrationInstance getMicroserviceInstance() { return new ConsulRegistrationInstance(consulInstance); } @Override public void init() { String instanceId = registrationId.getInstanceId(); consulInstance = new ConsulInstance(); consulInstance.setInstanceId(instanceId); consulInstance.setEnvironment(env); consulInstance.setApplication(BootStrapProperties.readApplication(environment)); consulInstance.setServiceName(BootStrapProperties.readServiceName(environment)); } @Override public void run() { LOGGER.info("ConsulRegistration run"); ImmutableRegistration.Builder registrationBuilder = ImmutableRegistration.builder() .name(consulInstance.getServiceName()); List<String> endpoints = consulInstance.getEndpoints(); for (String endpoint : endpoints) { Endpoint temp = EndpointUtils.parse(endpoint); if (temp.getAddress() instanceof URIEndpointObject) { String hostOrIp = ((URIEndpointObject) temp.getAddress()).getHostOrIp(); int port = ((URIEndpointObject) temp.getAddress()).getPort(); String serviceId = hostOrIp + ":" + port; consulDiscoveryProperties.setServiceId(serviceId); consulInstance.setServiceId(serviceId); registrationBuilder.id(serviceId); registrationBuilder.address(hostOrIp); registrationBuilder.port(port); break; } } List<String> tags = consulDiscoveryProperties.getTags(); if (CollectionUtils.isEmpty(tags)) { tags.add(consulInstance.getServiceName()); } registrationBuilder.tags(tags); Gson gson = new GsonBuilder().disableHtmlEscaping().create(); Map<String, String> meta = new HashMap<>(); meta.put("meta", gson.toJson(consulInstance)); registrationBuilder.meta(meta); ImmutableRegistration newService = registrationBuilder.build(); AgentClient agentClient = consulClient.agentClient(); agentClient.register(newService); LOGGER.info("ConsulRegistration newService"); }
- 实现服务发现功能
1) 查找Consul中的Service实例
2) 将Consul的Service实例转换为ConsulInstance,示例代码如下:@Override public List<ConsulDiscoveryInstance> findServiceInstances(String application, String serviceName) { logger.info("findServiceInstances application: {}, serviceName: {}", application, serviceName); consulDiscoveryInstanceList = getInstances(serviceName); return consulDiscoveryInstanceList; } public List<ConsulDiscoveryInstance> getInstances(@NotNull final String serviceName) { List<ConsulDiscoveryInstance> instances = new ArrayList<>(); addInstancesToList(instances, serviceName); return instances; } private void addInstancesToList(List<ConsulDiscoveryInstance> instances, String serviceName) { List<ServiceHealth> healthServices = getHealthServices(serviceName); if (!CollectionUtils.isEmpty(healthServices)) { for (ServiceHealth serviceHealth : healthServices) { Service service = serviceHealth.getService(); Gson gson = new GsonBuilder().disableHtmlEscaping().create(); ConsulInstance consulInstance = gson.fromJson(service.getMeta().get("meta"), ConsulInstance.class); instances.add(new ConsulDiscoveryInstance(consulInstance)); } } } private List<ServiceHealth> getHealthServices(String serviceName) { HealthClient healthClient = consulClient.healthClient(); return healthClient.getHealthyServiceInstances(serviceName).getResponse(); }
consul配置中心实现
- 新建config-consul模块
- 在pom中添加相关依赖,与consul有关的依赖如下
<dependency> <groupId>org.kiwiproject</groupId> <artifactId>consul-client</artifactId> <version>1.4.2</version> </dependency>
- 实现配置中心功能
1)创建ConsulConfigClient,示例代码:public ConsulConfigClient consulConfigClient(Environment environment) { ConsulConfigProperties consulConfigProperties = consulConfigProperties(environment); ConsulClient consulClient = consulClient(consulConfigProperties, consulRawClientBuilderSupplier()); return new ConsulConfigClient(updateHandler, environment, consulConfigProperties, consulClient); } @Override public PropertySource<?> create(Environment environment) { try { consulConfigClient = consulConfigClient(environment); consulConfigClient.refreshConsulConfig(); } catch (Exception e) { throw new IllegalStateException("Set up consul config failed.", e); } return new MapPropertySource(SOURCE_NAME, valueCache); }
- 在ConsulConfigClient中实现key-value的读取,示例代码如下:
private Map<String, Object> getValues(String path) { Map<String, Object> values = new HashMap<>(); KeyValueClient keyValueClient = consulClient.keyValueClient(); String decodedValue = keyValueClient.getValueAsString(path).orElseThrow(); if (StringUtils.isBlank(decodedValue)) { return values; } return getValues(path, decodedValue); } private @NotNull Map<String, Object> getValues(String path, String decodedValue) { Map<String, Object> values = new HashMap<>(); if (path.endsWith(".yaml") || path.endsWith(".yml")) { YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); yamlFactory.setResources(new ByteArrayResource(decodedValue.getBytes(StandardCharsets.UTF_8))); values.putAll(toMap(yamlFactory.getObject())); } else if (path.endsWith(".properties")) { Properties properties = new Properties(); try { properties.load(new StringReader(decodedValue)); } catch (IOException e) { LOGGER.error(e.getMessage(), e); } values.putAll(toMap(properties)); } else { values.put(path, decodedValue); } return values; }
- 自动获取最新内容,示例代码:
try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { cache.addListener(newValues -> { Optional<Value> newValue = newValues.values().stream() .filter(value -> value.getKey().equals(path)) .findAny(); newValue.ifPresent(value -> { Optional<String> decodedValue = newValue.get().getValueAsString(); decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("environmentData", getValues(path))).start()); }); }); cache.start(); }
功能测试
- 在demo模块下新建demo-consul模块,用户consul测试
- 编写完测试用例后,启动provider、gateway、consumer
- 启动test-client
- test-client运行完成后,观察控制台输出,没有输出测试相关错误的,表示测试通过
代码检查
- 代码检查要执行的命令
- 在本地执行除maven.yml文件的,有关maven命令和其它命令,需要安装插件或工具的自行参考有关工具的安装使用方法
- 执行maven.yml,由于在此过程中需要下载镜像,需要在能访问到有关镜像的机器上执行,同时要注意修改 test-client的pom.xml配置,使应用能够访问到对应的镜像里面的服务
提交代码
- 功能测试和代码检查都通过后,就可以提交代码了,代码提交的消息参考github代码提交规范
提交PR
- 在github代码仓库页面发起 Pull Request
- 提交PR后,告知相关人员执行代码检查
修复代码检视中发现的错误
- 提交PR后,相关人员会对代码进行检视,并提出修改意见
- 根据修改意见修改代码,并重新提交代码
- 重复以上步骤,直至代码被合并
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
评论(0)