【技术交流】SpringCloud项目接入华为云微服务引擎CSE(一)
1.项目Gitee地址
https://gitee.com/caichunfu/dtse-practice-microservice
2.运行环境
JDK1.8
Maven3.6.3
本地CSE引擎
下载地址:https://support.huaweicloud.com/devg-cse/cse_devg_0036.html
踩过的一些坑:
3.1 依赖导入报错
需要把Maven的中央仓库地址改为华为中央仓库地址,修改setting.xml文件
<mirror>
<id>huaweicloud</id>
<mirrorOf>*,!HuaweiCloudSDK</mirrorOf>
<url>https://repo.huaweicloud.com/repository/maven/</url>
</mirror>
3.2 版本问题
官网地址:https://github.com/huaweicloud/spring-cloud-huawei
1)使用Hoxton分支,项目启动时ribbon的启动类会报错,建议使用master分支
com.huaweicloud.servicecomb.discovery.ribbon.ServiceCombRibbonClientConfiguration required a bean of type 'com.netflix.client.config.IClientConfig' that could not be found.
2)master分支1.8.0-2020.0.x与网关存在兼容性问题,建议使用1.6.1-2020.0.x版本
3)master分支需要使用 springcloud 2020.x 的版本,这个版本移除了Netflix相关的组件,所以需要对组件进行替换
3.3 本次测试使用的版本
<properties>
<spring-boot.version>2.5.3</spring-boot.version>
<spring-cloud.version>2020.0.4</spring-cloud.version>
<spring-cloud-huawei.version>1.6.1-2020.0.x</spring-cloud-huawei.version>
<servicecomb.version>2.5.0</servicecomb.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
4.1 父工程引入
<dependencyManagement>
<!-- configure spring cloud huawei version -->
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>spring-cloud-huawei-bom</artifactId>
<version>${spring-cloud-huawei.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
4.2 子工程引入,并删除eureka相关依赖
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>spring-cloud-starter-huawei-service-engine</artifactId>
</dependency>
5.创建bootstrap.yml文件
spring:
application:
name: #微服务名
cloud:
servicecomb:
discovery:
enabled: true
address: http://127.0.0.1:30100
appName: #应用名
serviceName: ${spring.application.name}
version: 0.0.1
healthCheckInterval: 30
config:
serverAddr: http://127.0.0.1:30110
serverType: kie
6.启动类添加注解
@EnableDiscoveryClient
启动本地微服引擎,访问http://127.0.0.1:30103/进入微服务引擎管理界面,查看服务是否注册成功
7.启动前遇到的问题
7.1 报错信息:
could not be registered. A bean with that name has already been defined in URL
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
无法注册。URL中已经定义了一个具有该名称的bean
考虑重命名一个bean,或者通过设置Spring实现重写。主要的允许bean定义覆盖=true
解决方案:
yml配置文件添加配置
spring:
main:
allow-bean-definition-overriding: true
7.2 报错信息:
Action:
Correct the classpath of your application so that it contains a single, compatible version of io.micrometer.core.instrument.distribution.DistributionStatisticConfig$Builder
解决方案:
pom.xml里面的依赖包有重复,需要将重复的依赖包删除
7.3 报错信息:
此问题只会在使用Honxton版本时出现,建议使用master分支1.6.1-2020.0.x版本
Parameter 0 of method ribbonServerList in com.huaweicloud.servicecomb.discovery.ribbon.ServiceCombRibbonClientConfiguration required a bean of type 'com.netflix.client.config.IClientConfig' that could not be found.
Action:
Consider defining a bean of type 'com.netflix.client.config.IClientConfig' in your configuration.
解决方案:
IClientConfig 类,这个类定义时,不能直接 return 一个没有任何属性的 DefaultClientConfigImpl 对象, openFeign 会在源码里面使用这个对象,报空指针异常,如果要自己定义,需要初始化里面该有的属性
@Configuration
public class IClientConfig {
@Bean
public DefaultClientConfigImpl iClientConfig(){
//网上很多的错误写法
//return new DefaultClientConfigImp();
//加上getClientConfigWithDefaultValues初始化参数
return DefaultClientConfigImpl.getClientConfigWithDefaultValues();
}
}
8.Feign远程调用
使用SpringCloudHuawei做远程调用时会报错,可能兼容性存在问题
为了验证问题还原了SpringCloud项目,openfeign调用不会报错
报错信息:
org.apache.servicecomb.service.center.client.exception.OperationException: get service instances list fails, statusCode = 400; message = Bad Request; content = {"errorCode":"400012","errorMessage":"Micro-service does not exist","detail":"Consumer[30b75156753bc55385a7ae74d0611c77fc5f7522][development/dtse-practice-microservice/dtse-system/0.0.1] find provider[development/dtse-practice-microservice/default/0+] failed, provider does not exist"}
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: default
解决---暂未解决:
已对接后端技术专家,暂未解决,解决后会更新进度
9.改为RestTemplate方式调用
前端参数为MultipartFile和JSON,请求类型为POST
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String upLoadOneFile(@RequestPart("file") MultipartFile file, @RequestParam("obsParamsJson") String obsParamsJson) throws IOException {
OBSStorageParams obsParams = JSON.parseObject(obsParamsJson, OBSStorageParams.class);
String objURL = obsService.uploadOneFile(file.getInputStream(), obsParams);
return objURL;
}
RestTemplate调用代码
//请求url
String url = "http://xxxx:xxxx/xx/x";
//构造请求头
HttpHeaders httpHeaders = new HttpHeaders();
HttpHeaders headers = httpHeaders;
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
//FileSystemResource将文件变成流以发送
File file = MultipartFileToFile.multipartFileToFile(multipartFile);
FileSystemResource fileSystemResource = new FileSystemResource(file);
//构造请求体,使用LinkedMultiValueMap
MultiValueMap<String, Object> resultMap = new LinkedMultiValueMap<>();
resultMap.add("file", fileSystemResource);
resultMap.add("obsParamsJson", obsParamsJson);
//HttpEntity封装整个请求报文
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(resultMap, headers);
//postForObject发送请求体
String objURL = restTemplate.postForObject(url, httpEntity, String.class);
MultipartFile转File
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MultipartFileToFile {
/**
* MultipartFile 转 File
*
* @param file
* @throws Exception
*/
public static File multipartFileToFile(MultipartFile file) throws Exception {
File toFile = null;
if (file.equals("") || file.getSize() <= 0) {
file = null;
} else {
InputStream ins = null;
ins = file.getInputStream();
toFile = new File(file.getOriginalFilename());
inputStreamToFile(ins, toFile);
ins.close();
}
return toFile;
}
//获取流文件
private static void inputStreamToFile(InputStream ins, File file) {
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除本地临时文件
* @param file
*/
public static void delteTempFile(File file) {
if (file != null) {
File del = new File(file.toURI());
del.delete();
}
}
}
10.网关改造
官网的说明:
Spring Cloud Huawei Hoxton分支只提供Spring Cloud Gateway基于Ribbon的负载均衡,及其配套的基于流量治理和灰度发布功能。
Spring Cloud Huawei master(2020.0.x版本)分支只提供Spring Cloud Gateway基于Spring Cloud LoadBalance的负载均衡, 及其配套的基于流量治理和灰度发布功能。建议Spring Cloud Gateway升级到2020.0.x版本。
由于原项目使用的网关为Zuul,需要改为Spring Cloud Gateway
10.1 删除原项目zuul的依赖
添加springcloudgateway的依赖和springcloudhuawei提供的网关依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>spring-cloud-starter-huawei-service-engine-gateway</artifactId>
</dependency>
10.2 添加网关配置文件
spring:
main:
allow-bean-definition-overriding: true
cloud:
gateway:
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
#路由标识(id:标识,具有唯一性)
- id: dtse-system-route
# 目标服务地址(uri:地址,请求转发后的地址)
uri: lb://dtse-system
filters:
args:
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
- Path=/**
urifiler:
login-uri: /login
10.3 定义全局过滤器实现鉴权
package com.huaweicloud.filter;
import com.huaweicloud.commons.outhUtils.JwtUtil;
import com.huaweicloud.commons.response.ResultCode;
import com.huaweicloud.config.URIFilter;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class RouteConfiguration implements GlobalFilter, Ordered {
@Autowired
JwtUtil jwtUtil;
@Autowired
URIFilter uriFilter;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
RequestPath path = request.getPath();
// 2、登陆请求放行
if(path.value().contains(uriFilter.getLoginuri().get(0))){
System.out.println("登陆请求路经:request.getPath() = " + path.value());
log.info("登录");
return chain.filter(exchange);
}
//3、非登陆请求用户权限校验
String authorization = request.getHeaders().getFirst("Authorization");
if (!StringUtils.isEmpty((authorization))) {
System.out.println("非登陆请求路径:request.getPath() = " + path.value());
//2、获取请求头中Token信息
String token = authorization.replace("Bearer", "");
//3、Token校验
Claims claims = jwtUtil.parseToken(token) ;
//4、获取用户id,并将用户id传送到后端
if (claims == null) {
try {
throw new Exception(String.valueOf(ResultCode.UNAUTHENTICATED));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
String id = claims.getId();
//5、添加用户请求头
request.mutate().header("userId",id).build();
return chain.filter(exchange);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
10.4 urifiler配置类
@ConfigurationProperties(prefix = "urifiler", ignoreUnknownFields = false)
@Data
@Component
public class URIFilter {
private List<String> loginuri;
}
11.配置中心的使用
访问http://127.0.0.1:30103/进入微服务本地引擎管理界面
选择配置列表,创建配置项
- 点赞
- 收藏
- 关注作者
评论(0)