最近因为业务需要,要把项目上用了很久的 Eruka 换成 Consul。看了官网文档 和 各种 google、百度,遇到了很多坑,但是总算靠着 我 强大 的 推理能力 和 摸索(猜测)能力 把坑一个一个填平了。
在这里记录一下:现在的架构如下图所示(简化了部分):
如图所示:
1:项目上有一个外网服务器,和内网Nginx服务器连接路由器打通。
2:内网Nginx负载均衡到两个API平台。
3:API平台从Consul server获得服务注册列表。
4:所有的SpringBoot服务部署在Docker环境中。每个SpringBoot都使用8080端口,由docker分发不同的端口给每个SpringBoot服务。
5:部分服务由于需要,开启了 HTTPS 访问。
Consul详细部署方式如下:
1:启动Consul server:
nohup consul agent -data-dir=/opt/consul/data -server -bootstrap -ui -datacenter=dc1 -node=192.168.1.120 -bind=0.0.0.0 -client=192.168.1.120 &
启动Consul client1:
nohup consul agent -data-dir=/opt/consul/data -ui -datacenter=dc1 -node=192.168.1.110 -bind=192.168.1.110 -client=192.168.1.110 -join=192.168.1.120 &
启动Consul client2:
nohup consul agent -data-dir=/opt/consul/data -ui -datacenter=dc1 -node=192.168.1.119 -bind=192.168.1.119 -client=192.168.1.119 -join=192.168.1.120 &
此时访问 http://192.168.1.120:8500/ui/ 即可以看到如下界面:
SpringBoot服务端配置:
1:引入JAR包依赖:pom.xml
org.springframework.boot
spring-boot-starter-actuator
1.5.3.RELEASE
org.springframework.cloud
spring-cloud-starter-consul-discovery
增加如下配置: application.properties
spring.application.name=codeService
spring.cloud.consul.host=192.168.1.110
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.serviceName=COOLSERVICE
在启动类上添加 @EnableDiscoveryClient 注解
@EnableDiscoveryClient //支持服务发现
public class PlayRunApplication {
public static void main(String[] args) { SpringApplication.run(PlayRunApplication.class, args);}
}
启动SpringBoot服务后,查看Consul控制台,发现我的服务 Node Checks 正常,但是 Service Checks 报异常。
点进去看日志,报错信息如下:
Get http://localhost:8085/health: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
先开始还以为是防火墙导致的不可访问,关闭防火墙之后还是不行,跑去官网一顿挖掘,原来SpringBoot服务和Consul不在同一台导致Consul访问不到SpringBoot的服务。
需要添加如下配置: application.properties
spring.cloud.consul.discovery.prefer-ip-address=true
spring.cloud.consul.discovery.ip-address=192.168.1.123 # SpringBoot服务所在的ip
重启之后发现 Service Checks 依旧报错。看日志没有任何提示。
又继续Google一番,发现好多人说 是 spring-cloud-dependencies 版本太低的问题
我当前的版本是 Camden.SR3 ,这个版本确实太低,换成了推荐的 Edgware.SR2 版本。
重启之后,Service Checks 终于正常。
— 继续采坑中。
由于某些SpringBoot服务使用了HTTPS。Consul会自动在配置的IP或域名前面追加 http:// 因此导致服务不可用。
解决方案,开启SpringBoot 的 HTTPS 和 HTTP 同时访问功能:
HTTPS只需要添加一个域名证书配置: application.properties
#SSL证书路径 一定要加上classpath:
server.ssl.key-store=classpath:test.sunshuai.jks
#SSL证书密码
server.ssl.key-store-password=test.sunshuai
#证书类型
server.ssl.key-store-type=JKS
同时允许HTTP访问:
1:添加HTTP端口配置 application.properties :
server.http.port=8808
2:添加一个 Configuration 配置类
import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 当开启了 HTTPS 服务时,还需要 http 访问就需要这个
*/
@Configuration
public class HttpConfig {
@Value("${server.http.port}")
private int httpPort;
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
TomcatEmbeddedServletContainerFactory containerFactory = (TomcatEmbeddedServletContainerFactory) container;
Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
connector.setPort(httpPort);
containerFactory.addAdditionalTomcatConnectors(connector);
}
}
};
}
}
重启SpringBoot,测试HTTP,可用。但是Consul里面还是默认使用HTTPS的端口。
继续在 application.properties 配置中添加一行配置,让注册到Consul的端口和HTTP的端口相同:
spring.cloud.consul.discovery.port=8808
重启之后,一切正常。
注:Docker环境由于端口映射不同,比如物理机的 18808 映射到容器的 8808 所以要把 18808 端口注册到Consul:application.properties
server.port=8080
server.http.port=8808
spring.cloud.consul.discovery.port=18808
服务端到此一切正常。
开始搭建消费端:
引入jar依赖:
org.springframework.cloud
spring-cloud-starter-consul-discovery
注:spring-cloud-dependencies 的版本也使用 Edgware.SR2 及以上版本。
添加配置:application.properties
spring.application.name=spring-cloud-consul-consumer
spring.cloud.consul.host=192.168.1.120
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.register=false
测试获取服务注册列表和获取服务地址:
@Api(value="/test",description="测试",position = 4)
@RestController
@RequestMapping(value="/test",name = "测试")
public class TestController {
@Autowired
private LoadBalancerClient loadBalancer;
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/services")
public Object services() {
return discoveryClient.getInstances("COOLSERVICE");//获取所有服务
}
@RequestMapping("/discover")
public Object discover() {
return loadBalancer.choose("COOLSERVICE").getUri().toString();//从所有服务中选择一个服务(轮询)
}
}
浏览器访问:http://localhost:8888/test/services 和 http://localhost:8888/test/discover 都能正常拿到服务信息。
OVER!