记录一下搭建 Spring Cloud 过程中踩过的一些坑。写这篇随笔时候不知道为什么想到了看过的一个短片《断崖》,看的时候真的感受到了女主的绝望和无助。感觉自己就像女主一样,我在自己技术水平的坑里努力的爬着,好的是我爬出来了,坏的是外面还有一个更大的坑!!!人生路漫漫,且爬且珍惜!
Spring 版本
- Spring Boot:2.0.0.RELEASE
- Spring Cloud:Finchley.SR2
多环境配置
多配置的切换在开发中真是很常用,能有效提高效率。一些成熟的框架基本都有关于配置切换的解决方案,当然了 Spring Cloud 也不例外!
先看看我的配置文件结构:
配置文件通过后缀区分:dev - 开发环境配置,test - 测试环境配置,prod - 生产环境配置。
bootstrap.yml 文件来配置加载那个配置文件:
spring:
profiles:
active: dev
spring.profiles.active 配置的就是需要加载的配置文件,这样就能通过配置来加载不同的配置文件。
但是,我们一般都是打包成 jar 包来运行,这样分别打包还是有点麻烦,所以可以打包一个 jar 包,通过添加启动参数来加载不同的配置:
java -jar xxx.jar --spring.profiles.active=dev 表示使用开发环境的配置
java -jar xxx.jar --spring.profiles.active=test 表示使用测试环境的配置
java -jar xxx.jar --spring.profiles.active=prod 表示使用生产环境的配置
这里贴一个我封装的启动 jar 包的 shell 脚本:
#!/bin/bash JARURI=$1 # 第一个参数为jar包绝对路径 CONFENV=$2 # 第二个参数为配置环境 LOGSDIR=$3 # 第三个参数为日志目录 # 判断文件是否存在 if [ ! -f "$JARURI" ];then echo "ERROR:File does not exist;" exit 1 fi # 判断日志目录是否存在 if [ ! -d "$LOGSDIR" ];then echo "ERROR:Directory does not exist;" exit 1 fi # 判断配置环境 ENVARR=("dev" "test" "prod") if echo "${ENVARR[@]}" | grep -w "$CONFENV" &>/dev/null; then : else echo "ERROR:Env does not exist;" exit 1 fi # 启动 OLD_IFS="$IFS" IFS="/" file_array=($JARURI) IFS="$OLD_IFS" file_array_len=${#file_array[*]} file_name_index=`expr $file_array_len - 1` log_uri=${LOGSDIR%*/}/${file_array[$file_name_index]}_"$CONFENV"_$(date "+%Y_%b_%d_%H_%M_%S_%N").txt java -jar $JARURI > $log_uri --spring.profiles.active=$CONFENV &
注册中心安全认证
注册中心的页面是直接就能访问的,这肯定不是我们所希望的,所以需要添加安全机制进行保护,这里需要用到依赖 spring-boot-starter-security
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
配置文件增加 spring.security.user.name 和 spring.security.user.password 配置,修改注册中心地址:
eureka:
client:
service-url:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@192.168.1.254:9010/eureka/ # 注册中心地址
register-with-eureka: false # 是否注册到注册中心
fetch-registry: false # 是否拉取服务列表
server:
enable-self-preservation: true # 是否开启注册中心保护机制
eviction-interval-timer-in-ms: 60000 # 服务清理间隔,毫秒
instance:
prefer-ip-address: true # 是否使用IP地址广播服务
lease-renewal-interval-in-seconds: 30 # 服务租约时间,秒
lease-expiration-duration-in-seconds: 90 # 间隔多久时间没有发送心跳,认为服务不可用,秒
spring:
# 服务设置
application:
name: server-eureka
# 安全机制
security:
user:
name: eureka
password: 123456
# 服务设置
server:
port: 9010
使用此组件,安全是实现了,访问注册中心页面,需要登录用户名和密码了,但是问题也出现了,客户端服务一直注册不上去。我查找资料给出的解决方案是禁用 security 的 csrf,下面是处理方案,但是我使用此方案并不生效:
@EnableWebSecurity static class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http);//加这句是为了访问eureka控制台和/actuator时能做安全控制 http.csrf().disable(); } }
上面的方案我使用不生效,我使用下面的方案解决的:
package com.microview.servereureka.basic; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; /** * 注册中心basic认证 */ @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Value("${spring.security.user.name}") private String username; @Value("${spring.security.user.password}") private String password; @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .passwordEncoder(NoOpPasswordEncoder.getInstance()) .withUser(username).password(password) .authorities("ADMIN"); } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .disable() .authorizeRequests() .anyRequest().authenticated() .and() .httpBasic(); } }
容器宿主机IP注册
由于所有的项目都运行在 Docker 容器中,这就导致客户端注册到注册中心的 IP 地址为容器的虚拟 IP,无法通过外部访问,这里的解决方案是构建容器时候设置容器的网络模式为桥接模式。
docker-compose.yml 中配置 network_mode: "bridge"
客户端配置文件中相关配置(192.168.1.254 为宿主机IP地址):
eureka: client: service-url: defaultZone: http://eureka:[email protected]:9010/eureka/ instance: instance-id: ${eureka.instance.ip-address}:${server.port} ip-address: 192.168.1.254 prefer-ip-address: true
这样注册到注册中心的IP地址就是宿主机的IP了。
参考资料
https://blog.csdn.net/quanqxj/article/details/80592231
https://github.com/spring-cloud/spring-cloud-netflix/issues/2754
https://www.oschina.net/question/556985_2273961
https://blog.csdn.net/qq_36148847/article/details/79427878