SpringCloud技术指南系列(四)服务注册发现之Consul服务注册

蔚落 2021-11-11 16:50 549阅读 0赞

SpringCloud技术指南系列(四)服务注册发现之Consul服务注册

SpringCloud所谓的服务注册与发现,流程大致是:

  • 将Springboot微服务客户端项目的地址等信息,通过网络发送到注册中心,由注册中心保存下来。
  • 另一个客户端B访问已经注册到注册中心的服务A,通过注册中心提供的域名解析方式,解析出服务A的地址等信息。
  • 如果提供服务A的客户端有多个,就按照某个策略(比如轮询、负载均衡等)选取一个地址返回。
  • 客户端B访问注册中心返回的地址,获取结果,这里注意,是B直接访问A,而不是注册中心转发,因此要保证B和A是互通的。

目前服务发现的解决方案有Eureka,Consul,Zookeeper等,这三个是SpringCloud官方支持的。

前几篇已经讲了如何搭建Eureka的服务注册发现,本篇讲下Consul的服务注册如何实现,下一篇介绍下consul的服务如何调用。

代码可以在SpringBoot组件化构建https://www.pomit.cn/java/spring/springcloud.html中的ConsulServer组件中查看,并下载。

首发地址:

  品茗IT-同步发布

品茗IT提供在线支持:

  一键快速构建Spring项目工具

  一键快速构建SpringBoot项目工具

  一键快速构建SpringCloud项目工具

  一站式Springboot项目生成

  Mysql一键生成Mybatis注解Mapper

  Mysql一键生成SpringDataRest项目

如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以加入我们的java学习圈,点击即可加入,共同学习,节约学习时间,减少很多在学习中遇到的难题。

一、Consul注册中心

首先,到Consul官网下载Consul注册中心:https://www.consul.io/downloads.html;

Consul官网给出了一个consul的界面示例:https://demo.consul.io/ui/

下载完成后,无需安装,直接启动即可:

dev模式启动(普通开发使用,不带记忆功能):./consul agent -dev

线上环境启动(因App而异):./consul agent -server -bootstrap-expect 1 -data-dir=/usr/local/consul/data -bind=10.247.62.76 -client=0.0.0.0 -ui

  • server agent模式是server,agent可以运行为server或client模式。client是一个非常轻量级的进程.用于注册服务,运行健康检查和转发对server的查询.
  • bootstrap-expect 表示单节点启动
  • data-dir 数据存放位置
  • bind 绑定的地址
  • client 允许的客户端ip
  • ui 带界面

启动之后,就可以用来做服务发现注册了。consul只是用来存储服务的实时信息与地址的。

二、引入依赖

需要引入spring-boot-starter-web和spring-cloud-starter-consul-discovery。

依赖如下:

  1. <?xml version="1.0"?>
  2. <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  3. <modelVersion>4.0.0</modelVersion>
  4. <parent>
  5. <groupId>cn.pomit</groupId>
  6. <artifactId>springcloudwork</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. </parent>
  9. <artifactId>ConsulServer</artifactId>
  10. <name>ConsulServer</name>
  11. <url>http://maven.apache.org</url>
  12. <properties>
  13. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  14. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  15. <java.version>1.8</java.version>
  16. <maven-jar-plugin.version>2.6</maven-jar-plugin.version>
  17. </properties>
  18. <dependencies>
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-web</artifactId>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.springframework.cloud</groupId>
  25. <artifactId>spring-cloud-starter-consul-discovery</artifactId>
  26. </dependency>
  27. </dependencies>
  28. </project>

父模块pom文件可以在https://www.pomit.cn/spring/SpringCloudWork/pom.xml获取。

三、配置Consul服务注册

这里使用yaml文件写配置,application.yml:

  1. server:
  2. port: 8811
  3. spring:
  4. application:
  5. name: consulServer
  6. cloud:
  7. consul:
  8. host: 127.0.0.1
  9. port: 8500
  10. discovery:
  11. prefer-ip-address: true
  12. healthCheckPath: /consul/health

这里面,包含了端口、应用名、consul注册中心信息、注册方式、健康检查路径。

spring.application.name是标识了应用名,注册到consul之后,显示的就是它。

spring.cloud.consul.discovery.prefer-ip-address是使用ip地址,如果不写它,默认是域名,那样测试起来很麻烦。

spring.cloud.consul.discovery.healthCheckPath这个,consul做健康检查的路径。

spring.cloud.consul.port consul的端口。8500 端口基于 HTTP 协议,用于 API 接口或 WEB UI 访问。

四、启动服务注册

3.1 启动类

使用@EnableDiscoveryClient注解启动类, @EnableDiscoveryClient是将项目作为客户端注册到注册中心的注解,开启服务发现功能。

ConsulServerApplication:

  1. package cn.pomit.springbootwork.consulserver;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  5. @EnableDiscoveryClient
  6. @SpringBootApplication
  7. public class ConsulServerApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(ConsulServerApplication.class, args);
  10. }
  11. }

3.2 开放服务

下面提供了个web接口做服务,访问http://127.0.0.1:8858/consulApi/ip ,可以获取当前请求ip和服务器ip地址。由于我们已经使用了consul,其他应用已经可以通过服务名找到本应用,并通过服务发现访问http://consulServer/consulApi/ip 来访问改接口(注意,普通http请求这样写是访问不到的,只有通过服务注册发现调用,走consul的域名服务才能这样访问)。

ConsulServerRest :

  1. package cn.pomit.springbootwork.consulserver.web;
  2. import javax.servlet.http.HttpServletRequest;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RequestMethod;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import cn.pomit.springbootwork.consulserver.model.IpModel;
  7. import cn.pomit.springbootwork.consulserver.model.ResultModel;
  8. import cn.pomit.springbootwork.consulserver.util.IPUtil;
  9. @RestController
  10. @RequestMapping("/consulApi")
  11. public class ConsulServerRest {
  12. @RequestMapping(value = "/ip", method = { RequestMethod.GET })
  13. public ResultModel welCome(HttpServletRequest request) {
  14. IpModel ipModel = new IpModel();
  15. ipModel.setClientIpAddress(IPUtil.getIpAddr(request));
  16. ipModel.setServerIpAddress(IPUtil.localIp());
  17. return ResultModel.ok(ipModel);
  18. }
  19. }

3.3 健康检查

前面写了我们健康检查地址是/consul/health,这里要开放个接口,让consul来检查身体。

  1. package cn.pomit.springbootwork.consulserver.web;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RequestMethod;
  4. import org.springframework.web.bind.annotation.RestController;
  5. @RestController
  6. @RequestMapping("/consul")
  7. public class HealthWeb {
  8. @RequestMapping(value = "health", method = { RequestMethod.GET })
  9. public String health() {
  10. return "check health";
  11. }
  12. }

五、过程中用到的工具类和实体

过程中用到了ResultModel实体和ResultCode枚举类,如果作为测试来用,其实没必要,这里是为后续章节做铺垫,将实体统一化,所以才多出了ResultModel实体和ResultCode枚举类。

IPUtil:

  1. package cn.pomit.springbootwork.consulserver.util;
  2. import java.net.InetAddress;
  3. import java.net.UnknownHostException;
  4. import javax.servlet.http.HttpServletRequest;
  5. public class IPUtil {
  6. /** * @Description: 获取客户端IP地址 */
  7. public static String getIpAddr(HttpServletRequest request) {
  8. String ip = request.getHeader("x-forwarded-for");
  9. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  10. ip = request.getHeader("Proxy-Client-IP");
  11. }
  12. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  13. ip = request.getHeader("WL-Proxy-Client-IP");
  14. }
  15. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  16. ip = request.getRemoteAddr();
  17. if (ip.equals("127.0.0.1")) {
  18. // 根据网卡取本机配置的IP
  19. InetAddress inet = null;
  20. try {
  21. inet = InetAddress.getLocalHost();
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. ip = inet.getHostAddress();
  26. }
  27. }
  28. // 多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
  29. if (ip != null && ip.length() > 15) {
  30. if (ip.indexOf(",") > 0) {
  31. ip = ip.substring(0, ip.indexOf(","));
  32. }
  33. }
  34. return ip;
  35. }
  36. /** * 获取的是本地的IP地址 * * @return */
  37. public static String localIp() {
  38. String result = "";
  39. try {
  40. InetAddress address = InetAddress.getLocalHost();
  41. result = address.getHostAddress();
  42. } catch (UnknownHostException e) {
  43. e.printStackTrace();
  44. }
  45. return result;
  46. }
  47. }

IpModel :

  1. package cn.pomit.springbootwork.consulserver.model;
  2. public class IpModel {
  3. private String clientIpAddress;
  4. private String serverIpAddress;
  5. public String getClientIpAddress() {
  6. return clientIpAddress;
  7. }
  8. public void setClientIpAddress(String clientIpAddress) {
  9. this.clientIpAddress = clientIpAddress;
  10. }
  11. public String getServerIpAddress() {
  12. return serverIpAddress;
  13. }
  14. public void setServerIpAddress(String serverIpAddress) {
  15. this.serverIpAddress = serverIpAddress;
  16. }
  17. }

ResultModel:

  1. package cn.pomit.springbootwork.consulserver.model;
  2. /** * @author cff */
  3. public class ResultModel {
  4. private String errorCode;
  5. private String message;
  6. private Object data;
  7. public ResultModel() {
  8. }
  9. public ResultModel(String errorCode, String message) {
  10. this.errorCode = errorCode;
  11. this.message = message;
  12. }
  13. public ResultModel(String errorCode, String message, Object data) {
  14. this.errorCode = errorCode;
  15. this.message = message;
  16. this.data = data;
  17. }
  18. public ResultModel(ResultCode resultCodeEnum, Object data) {
  19. this.errorCode = resultCodeEnum.getCode();
  20. this.message = resultCodeEnum.getDesc();
  21. this.data = data;
  22. }
  23. public ResultModel(ResultCode resultCodeEnum) {
  24. this.errorCode = resultCodeEnum.getCode();
  25. this.message = resultCodeEnum.getDesc();
  26. }
  27. public String geterrorCode() {
  28. return errorCode;
  29. }
  30. public void seterrorCode(String errorCode) {
  31. this.errorCode = errorCode;
  32. }
  33. public String getMessage() {
  34. return message;
  35. }
  36. public void setMessage(String message) {
  37. this.message = message;
  38. }
  39. public Object getData() {
  40. return data;
  41. }
  42. public void setData(Object data) {
  43. this.data = data;
  44. }
  45. public static ResultModel ok() {
  46. return new ResultModel(ResultCode.CODE_00000);
  47. }
  48. public static ResultModel ok(Object data) {
  49. return new ResultModel(ResultCode.CODE_00000, data);
  50. }
  51. public static ResultModel error() {
  52. return new ResultModel(ResultCode.CODE_00001);
  53. }
  54. public static ResultModel error(String msg) {
  55. return new ResultModel(ResultCode.CODE_00001.getCode(), msg);
  56. }
  57. public static ResultModel error(String msg, Object data) {
  58. return new ResultModel(ResultCode.CODE_00001.getCode(), msg, data);
  59. }
  60. public static ResultModel unAuth() {
  61. return new ResultModel(ResultCode.CODE_40004);
  62. }
  63. }

ResultCode:

  1. package cn.pomit.springbootwork.consulserver.model;
  2. /** * 响应码及其描述 Created by txl on 15/7/9. */
  3. public enum ResultCode {
  4. /** * 通用 */
  5. CODE_00000("00000", "操作成功"), CODE_00001("00001", "请求失败"), CODE_00002("00002", "错误的请求方法"), CODE_00003("00003", "非法的参数字段"), CODE_00004("00004", "异常抛出"), CODE_00005("00005", "权限不足"), CODE_00006("00006", "分页limit参数错误"), CODE_00007("00007", "分页offset参数错误"), CODE_00009("00009", "请求过于频繁"), CODE_00010("00010", "数据已存在"), CODE_00011("00011", "数据不存在"), CODE_00012("00012", "参数缺失"), CODE_00013("00013", "系统维护中"), CODE_00014("00014", "token缺失"), CODE_00015("00015", "token失效"), CODE_00016("00016", "签名错误"),
  6. CODE_10000("10000", "操作部分成功"),
  7. /** * 系统 */
  8. CODE_30000("30000", "系统ID错误"),
  9. /** * 授权 */
  10. CODE_40001("40001", "用户未找到"), CODE_40002("40002", "该用户状态异常"), CODE_40003("40003", "该用户已被删除"), CODE_40004("40004", "授权异常"),
  11. CODE_99999("99999", "签名无效");
  12. private String code;
  13. private String desc;
  14. ResultCode(String code, String desc) {
  15. this.code = code;
  16. this.desc = desc;
  17. }
  18. public String getCode() {
  19. return code;
  20. }
  21. public String getDesc() {
  22. return desc;
  23. }
  24. /** * 根据code匹配枚举 * * @param code * @return */
  25. public static ResultCode getResultCodeByCode(String code) {
  26. for (ResultCode resultCode : ResultCode.values()) {
  27. if (code.equals(resultCode.getCode())) {
  28. return resultCode;
  29. }
  30. }
  31. return null;
  32. }
  33. public static ResultCode getResultCodeByDesc(String desc) {
  34. for (ResultCode resultCode : ResultCode.values()) {
  35. if (desc.equals(resultCode.getDesc())) {
  36. return resultCode;
  37. }
  38. }
  39. return null;
  40. }
  41. }

快速构建项目

Spring组件化构建

SpringBoot组件化构建

SpringCloud服务化构建

喜欢这篇文章么,喜欢就加入我们一起讨论SpringBoot使用吧!
品茗IT交流群

发表评论

表情:
评论列表 (有 0 条评论,549人围观)

还没有评论,来说两句吧...

相关阅读