使用Consul作为微服务的注册中心

原文地址

原文链接

前言

看了相关的资料,我们选择consul作为注册中心,不用nacos的原因是nacos太大了,不用eureka的原因是已经停止维护,其他的Istio,Linkerd等都是主要基于Kubernetes的,所以我们这里选择consul,参考1参考2参考3

部署

基本部署

官方文档

首先,根据官方对于架构的介绍,我们至少需要一个server,甚至可以不需要client,也可以完成功能,我们这里使用单个server的方案。虽然多个server可用保证可用性,参考官网文档的Consensus Protocol部分,我们应当使用3个server节点去部署。但是这个对于单服务这种部署是没有意义的。因为单服务中,我们自己的服务只能注册到其中一个服务器,那么如果这个服务器挂了,我们在这个服务器上所有注册的服务都挂了。他的容灾策略从我目前所看到的文档总结来说,如果不增加一层自主转发的情况下(比如nginx?我没有具体尝试,不是特别确定是否可行),是保证不同服务器的数据互通以及服务器对外输出的高可用,但是起码对于spring-cloud而言,单个服务实例只能注册到一个服务器上,虽然他能发现本集群的其他服务器上注册的服务,但是如果该服务器一旦挂掉,上面注册的服务实例也会挂掉,所以在单台服务器下这种部署是没有意义的,这里可以参考Registering Multiple Same-Host ServicesA question about the registration of the consul cluster。这里官方Docker部署文档,并没有进行相关说明,甚至其他其余的两个服务都没有开放注册端口,非常的误导人

这里也给出多server和client的方案以防万一

version: "3.7"
services:
  consul-server-y:
    image: 'hashicorp/consul:latest'
    restart: always
    container_name: 'consul-server-y'
    hostname: 'consul-server-y'
    ports:
      - '9527:8500'
      - '19527:8600/tcp'
      - '19527:8600/udp'
    volumes:
      - ./server-y.json:/consul/config/server-y.json:ro
      - ./certs/:/consul/config/certs/:ro
    command: "agent -bootstrap-expect=3"

  consul-server-x:
    image: 'hashicorp/consul:latest'
    restart: always
    container_name: 'consul-server-x'
    hostname: 'consul-server-x'
    ports:
      - '9528:8500'
      - '19528:8600/tcp'
      - '19528:8600/udp'
    volumes:
      - ./server-x.json:/consul/config/server-x.json:ro
      - ./certs/:/consul/config/certs/:ro
    command: "agent -bootstrap-expect=3"
    
  consul-server-z:
    image: 'hashicorp/consul:latest'
    restart: always
    container_name: 'consul-server-z'
    hostname: 'consul-server-z'
    ports:
      - '9529:8500'
      - '19529:8600/tcp'
      - '19529:8600/udp'
    volumes:
      - ./server-z.json:/consul/config/server-z.json:ro
      - ./certs/:/consul/config/certs/:ro
    command: "agent -bootstrap-expect=3"
    
  consul-client:
    image: 'hashicorp/consul:latest'
    container_name: consul-client
    restart: always
    volumes:
      - ./client.json:/consul/config/client.json:ro
      - ./certs/:/consul/config/certs/:ro
    command: "agent"

服务端配置文件,三个配置文件都差不多,照葫芦画瓢稍微改下就行

{
  "node_name": "consul-server-x",
  "server": true,
  "ui_config": {
    "enabled": true
  },
  "retry_join": ["consul-server-y", "consul-server-z"],
  "data_dir": "/consul/data",
  "addresses": {
    "http": "0.0.0.0"
  },
  "encrypt": "aPuGh+5UDskRAbkLaXRzFoSOcSM+5vAK+NEYOWHJH7w=",
  "verify_incoming": false,
  "verify_outgoing": false,
  "verify_server_hostname": false
}

客户端配置文件

{
  "node_name": "consul-client",
  "data_dir": "/consul/data",
  "retry_join": ["consul-server-y", "consul-server-x", "consul-server-z"],
  "encrypt": "aPuGh+5UDskRAbkLaXRzFoSOcSM+5vAK+NEYOWHJH7w=",
  "verify_incoming": false,
  "verify_outgoing": false,
  "verify_server_hostname": false
}

这里如果需要保证高可用,服务需要启动三个实例,分别注册到9527,9528,9529端口,这样才能保证在其中一个server挂掉的情况,我们自己服务的高可用,或者只启动一个实例使用中间层做转发,但是如果这样consul服务的功能就看起来不是很完整了

官方更多配置命令行启动相关参数

ACL

Access Control Lists,需要配置注册中心的访问权限,首先需要添加访问权限在server的json配置文件中增加

{
  "acl":{
    "enabled": true,
    "default_policy":"deny",
    "enable_token_persistence": true
  }
}
  • enabled 是否开启ACL
  • default_policy 自定义权限需要将其设置为deny
  • enable_token_persistence token持久化到磁盘

然后进入集群内任意server中,执行命令

consul acl bootstrap

获取SecretID,然后在登录框内输入密钥id即可

由于我们这里修改了默认的策略,所以此时获得的SecretID只能用来登录,并不能用来注册,我们需要配置角色和权限来生成可以注册的密钥。当然如果简单使用,不设置default_policy,使用默认allow然后拿生成的SecretID就可以直接注册了

我们这里登录ui进行先新建策略,可以将策略定在node(server/client)上,也可以定在服务或者配置上,参考官方策略配置,我们这里需要服务注册的token所以定在服务上,这里如果需要完成服务的注册和发现,还需要对于node节点有读权限,否则无法发现其他服务

node_prefix "" {
  policy = "read"
}
service_prefix "" {
  policy = "write"
}

更多配置参考官方文档

然后再去角色页面新建角色,配置角色所拥有的策略选择刚才新建的策略,最后新建token选择刚才的角色即可(当然也可以不建角色角色直接配置策略在token上),至此我们获得一个可以用于服务注册的token

Gossip encryption

我们需要在集群中配置相同的密钥encrypt才能保证集群通信的可用性,我们现在需要生成自己的密钥,在容器内执行命令

 consul keygen
 #ABCDEFGHIKOPF123456781234567=

获取密钥,然后在各个server中添加配置文件即可

{
  "encrypt": "ABCDEFGHIKOPF123456781234567=",
  "encrypt_verify_incoming": true,
  "encrypt_verify_outgoing": true
}

mTLS

参考官方文档,进入任意server执行命令

consul tls ca create
consul tls cert create -server -dc dc1

然后在宿主机将文件拷贝出来,放入之前设置的docker映射卷目录中

docker cp  e36:dc1-server-consul-0-key.pem ./
docker cp  e36:dc1-server-consul-0.pem ./
docker cp  e36:consul-agent-ca.pem ./

这里需要注意拷贝出来的文件的权限问题,否则docker容器可能没有读权限

chmod 644 dc1-server-consul-0-key.pem 

最后增加mTLS相关认证配置在server和client配置文件中

{
  "verify_incoming": true,
  "verify_outgoing": true,
  "verify_server_hostname": true,
  "ca_file": "/consul/config/certs/consul-agent-ca.pem",
  "cert_file": "/consul/config/certs/dc1-server-consul-0.pem",
  "key_file": "/consul/config/certs/dc1-server-consul-0-key.pem"
}

HTTPS

看下SSL和mTLS的关系,具体配置这里就不赘述了,比较简单也不是必须的。运营商申请子域名,配ssl证书,最后配一下nginx,然后关闭测试端口,这样我们基本的生产环境的的consul就配置完成了

服务的注册和发现

我们这里选择使用Spring Cloud的项目进行服务注册和发现,这里核心参考spring-cloud-consul官方文档

我们使用openfeign进行服务间调用,故而这里引用的核心依赖有

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
<!--有一部分文档这里引用的是spring-cloud-starter-consul-all,不建议这么做,会导致部分配置属性如enabled功能失效 
参考 https://github.com/spring-cloud/spring-cloud-consul/issues/309-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
<!--识别bootstrap文件了-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
<!--如果作为基础服务就不需要openfeign了-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

首先启动类增加注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

添加consul相关配置

spring:
  cloud:
    consul:
      enabled: true
      #你的域名或者你的服务器ip地址
      host: your_domin_or_your_ip_address
      #这里如果使用nginx转发则为80或者443端口,否则为你的docker-compose中配置的端口,记得配置防火墙
      port: 443
      discovery:
      #如果有需要这里需要配置不同实例标识
        instance-id: ${spring.application.name}
        #如果无法从consul地址ping到service地址,那么consul的健康检测是无法通过的,需要持续由服务去ping到consul来保证服务的可用性
        #但是目前版本心跳的ping并不会带上token需要写一个切面去处理
        #正式环境不建议这么写,consul+springcloud目前我的使用情况是,使用心跳注册在服务关闭时候无法检测到服务关闭仍然在active状态原因不明
        #所以以服务稳定为前提最好还是使用host+port+healthPath的方式在证书环境注册
        heartbeat:
          enabled: true
         #在ACL步骤配置的可以用于服务注册的token
        acl-token: your_token
        #如果这里nginx配置了证书,那么我们需要HTTPS协议,默认HTTP不用设置
      scheme: HTTPS

如果服务死亡,springcloudconsul在目前的4.0.2的版本还不支持consul的deregister_critical_service_after字段去自动取消注册,起码在我的环境中health-check-critical-timeout等这种字段是不能如期望生效的

	/**
	 * Timeout to deregister services critical for longer than timeout (e.g. 30m).
	 * Requires consul version 7.x or higher.
	 */
	private String healthCheckCriticalTimeout;

导致死掉的服务仍然会在平衡策略中被请求到,目前没有比较好的处理的方法,只能手动删除,或者写定时任务脚本,参考Consul not deregistering zombie servicesConsul deregister ‘failing’ services,以及其他参考1参考2参考3,这里摘录脚本(不推荐写脚本,有这功夫不如手写注册模块)

leader="$(curl http://ONE-OF-YOUR-CLUSTER:8500/v1/status/leader | sed 

's/:8300//' | sed 's/"//g')"
while :
do
serviceID="$(curl http://$leader:8500/v1/health/state/critical | ./jq '.[0].ServiceID' | sed 's/"//g')"
node="$(curl http://$leader:8500/v1/health/state/critical | ./jq '.[0].Node' | sed 's/"//g')"
echo "serviceID=$serviceID, node=$node"
size=${#serviceID}
echo "size=$size"
if [ $size -ge 7 ]; then
curl --request PUT http://$node:8500/v1/agent/service/deregister/$serviceID
else
break
fi
done
curl http://$leader:8500/v1/health/state/critical

还是聊一句,spring-cloud-consul的维护进度确实太慢了,看了一些issue基本有问题就是欢迎PR,所以有不忙的兄弟可以去PR,如果项目比较重要的目前阶段还是建议手写注册模块,使用他的配置去注册坑实在是有点多

如果由consul可以ping到service地址且是通过hostname可以调用到,那么则不需要任何连接配置,如果不是通过host则需要在discovery中提示service的地址,参考此问题的说明。如果不能ping到,那么为了维持服务在consul的可用标志,我们需要让服务不断给consul发心跳,这种情况下由于consul本身的问题,在目前版本并不会带上acl-token,我们这里给出一个切面处理,代码来源该问题已经在2023.3.29的spring-cloud-starter-consul-discovery:4.0.2版本中修复,不需要再增加切面

@Aspect
@Configuration
@RequiredArgsConstructor
@ConditionalOnConsulEnabled
@ConditionalOnProperty("spring.cloud.consul.discovery.acl-token")
@Slf4j
public class ConsulClientAspect {
    private final ConsulDiscoveryProperties consulDiscoveryProperties;

    @PostConstruct
    public void init() {
        log.info("Hooking ConsulClient.agentCheckPass(String) calls to enforce acl token passing");
    }

    /**
     * Trap calls made to {@link ConsulClient#agentCheckPass(String)} and call the overridden method variant with ACL
     * token
     */
    @SneakyThrows
    @Around("execution (* com.ecwid.consul.v1.ConsulClient.agentCheckPass(String))")
    public Response<Void> trapAgentCheckPass(final ProceedingJoinPoint joinPoint) {
        final String checkId = (String) joinPoint.getArgs()[0];
        final ConsulClient client = (ConsulClient) joinPoint.getThis();
        return client.agentCheckPass(checkId, null, this.consulDiscoveryProperties.getAclToken());
    }
}

原文地址

原文链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
搭建微服务项目一般需要以下步骤: 1. 创建父项目 在项目根目录下创建一个父项目,作为所有微服务子项目的父项目。在父项目的pom.xml文件中,配置Spring Cloud和其他依赖的版本号,如下所示: ``` <dependencyManagement> <dependencies> <!-- Spring Cloud dependencies --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2020.0.2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` 2. 创建微服务子项目 在父项目下创建多个微服务子项目,每个子项目都是一个独立的服务模块,可以有自己的数据模型、业务逻辑、数据库等。每个子项目都需要配置Spring Boot和Spring Cloud相关依赖,如下所示: ``` <dependencies> <!-- Spring Boot dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Cloud dependencies --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> </dependencies> ``` 3. 配置微服务 每个微服务都需要有自己的配置文件,例如application.yml或application.properties。在配置文件中,需要指定微服务的端口号、注册中心地址、数据源等信息。 ``` server: port: 8080 spring: application: name: user-service datasource: url: jdbc:mysql://localhost:3306/user_db username: root password: root eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ ``` 4. 注册微服务 微服务需要注册注册中心,以便其他微服务可以发现它并调用它的接口。可以使用Eureka或Consul等开源组件作为注册中心。在微服务的配置文件中,需要指定注册中心的地址和端口号。 ``` eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ ``` 5. 调用微服务 微服务之间可以通过HTTP或RPC调用接口进行通信。可以使用Feign或Ribbon等Spring Cloud组件来实现微服务之间的调用。 例如,在一个微服务中调用另一个微服务的接口,可以使用以下代码: ``` @FeignClient(name = "user-service") public interface UserServiceClient { @GetMapping("/users/{id}") User getUserById(@PathVariable("id") Long id); } ``` 以上就是使用Spring Cloud搭建微服务项目的基本步骤。当然,在实际项目中还需要考虑一些其他因素,例如安全性、性能、可伸缩性等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值