-
### 一、SpringCloud项目架构介绍 #### 1.项目地址: [https://gitee.com/caichunfu/dtse-practice-microservice](https://gitee.com/caichunfu/dtse-practice-microservice) #### 2.项目技术栈如下: 1. 注册中心:Eureka 2. 网关:zuul 3. 声明式调用:feign 4. 存储:OBS、RDS for MySql  #### 3.项目结构: 1. dtse-commons:项目的公共工具类 2. dtse-eureka-server:eureka注册中心集群 3. dtse-feign-client:feign调用客户端 4. dtse-obs-storage:服务端,完成对obs的操作,包括上传和删除obs对象 5. dtse-system:消费端,实现用户登录、查询用户头像、更新用户头像(调用服务端请求) 6. dtse-zuul-getway:项目网关 ### 二、SpringCloud Huawei改造方案 #### 1.替换技术栈对比 | SpringCloud | SpringCloud-Huawei | | ------------------ | ------------------------------------------------------------ | | Eureka注册中心 | [华为云CSE微服务引擎](https://support.huaweicloud.com/devg-cse/cse_devg_0010.html) | | zuul网关 | SpringCloud Huawei Getway | | SpringCloud config | [CSE Config](https://support.huaweicloud.com/devg-cse/cse_devg_0022.html) | #### 2.SpringCloud Huawei技术文档 [https://github.com/huaweicloud/spring-cloud-huawei](https://github.com/huaweicloud/spring-cloud-huawei) #### 3.SpringCloud Huawei基础Demo [https://github.com/huaweicloud/spring-cloud-huawei-samples/tree/master/basic](https://github.com/huaweicloud/spring-cloud-huawei-samples/tree/master/basic) #### 4.SpringCloud Huawei版本对照  #### 5.改造前设置 1. 下载本地CSE,下载地址:[https://support.huaweicloud.com/devg-cse/cse_devg_0036.html](https://support.huaweicloud.com/devg-cse/cse_devg_0036.html) 2. 将maven镜像改为华为镜像仓库: ``` <mirror> <id>huaweicloud</id> <mirrorOf>*</mirrorOf> <url>https://repo.huaweicloud.com/repository/maven/</url> </mirror> ``` 3. 项目版本选定 - SpringBoot:2.3.7.RELEASE - SpringCloud:Hoxton.SR9 - SpringCloud Huawei:1.8.0-Hoxton #### 6.改造方案 1. 移除微服务项目里所有的eureka依赖,删掉application.yml里的eureka的配置 ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> ``` 2. 导入SpringCloud Huawei依赖 - 父工程里导入 ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> ``` - 子工程里导入 ```xml <dependency> <groupId>com.huaweicloud</groupId> <artifactId>spring-cloud-starter-huawei-service-engine</artifactId> </dependency> ``` 3. 添加bootstrap.yaml配置文件 ```yaml spring: application: name: #微服务名 cloud: servicecomb: discovery: enabled: true address: http://127.0.0.1:30100 appName: basic-application serviceName: ${spring.application.name} version: 0.0.1 healthCheckInterval: 30 config: serverAddr: http://127.0.0.1:30110 serverType: kie ``` 4. 解决启动中的报错 4.1. 需要编写IClientConfig配置类:   4.2. 配置类添加   4.3. 微服务里无数据库配置:  需要在启动类上加`@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})` 4.4. feign调用报错  SpringCloud Huawei当前版本对负载均衡支持不够友好,openfeign自带负载均衡,建议使用RestTemplate *以下解决方法来自本站的帖子[SpringCloud项目接入华为云微服务引擎CSE(一)](https://bbs.huaweicloud.com/forum/thread-178657-1-1.html)* 4.5. 修改openfeign调用为RestTemplate - 在dtse-system--usercontroller--UpdateIconImage路径对应的方法里注释掉openfeign调用代码 - 添加RestTemplate方式调用的代码 ```java //请求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:** ```java 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(); } } } ``` 4.6 网关改造 - 删除原项目zuul的依赖,添加springcloudgateway的依赖和springcloudhuawei提供的网关依赖 ```xml <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> ``` - 添加网关配置文件(注意uri不要用lb地址,当前版本涉及负载均衡的均有问题,暂时用固定地址) ```yaml 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 #不要用lb地址 uri: http://localhost:9090 filters: args: # 路由条件(predicates:断言,匹配 HTTP 请求内容) predicates: - Path=/** urifiler: login-uri: /login ``` - 定义全局过滤器实现鉴权 ```java @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; } } ``` - urifiler配置类 ```java @ConfigurationProperties(prefix = "urifiler", ignoreUnknownFields = false) @Data @Component public class URIFilter { private List<String> loginuri; } ``` 4.7 配置中心 在浏览器输入http://127.0.0.1:30103/,进入cse可视化界面,选择配置列表,创建配置项 
-
log4j:WARN No appenders could be found for logger (org.springframework.core.io.support.PathMatchingResourcePatternResolver).log4j:WARN Please initialize the log4j system properly.log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.2022-02-05 22:18:57,267 [ERROR] Can not output merged.log4j.properties,because can not write to directory of file file:/C:/Users/n00511724/.m2/repository/org/apache/servicecomb/transport-rest-vertx/1.2.0.B006/transport-rest-vertx-1.2.0.B006.jar!/config/base/log4j.properties org.apache.servicecomb.foundation.common.utils.Log4jUtils.outputFile(Log4jUtils.java:104)2022-02-05 22:18:57,285 [INFO] Found main class "helloworld.demo.provider.ProviderMain". org.apache.servicecomb.foundation.common.utils.JvmUtils.findMainClass(JvmUtils.java:94)2022-02-05 22:18:57,285 [INFO] Found main class "helloworld.demo.provider.ProviderMain" by stackTrace. org.apache.servicecomb.foundation.common.utils.JvmUtils.findMainClassByStackTrace(JvmUtils.java:63)2022-02-05 22:18:57,285 [INFO] Scb scan package list: org.apache.servicecomb,helloworld.demo.provider org.apache.servicecomb.foundation.common.utils.BeanUtils.prepareServiceCombScanPackage(BeanUtils.java:93)2022-02-05 22:18:57,303 [INFO] Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@50f8360d: startup date [Sat Feb 05 22:18:57 CST 2022]; root of context hierarchy org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:583)2022-02-05 22:18:57,371 [INFO] Loading XML bean definitions from URL [jar:file:/C:/Users/n00511724/.m2/repository/org/apache/servicecomb/foundation-config/1.2.0.B006/foundation-config-1.2.0.B006.jar!/META-INF/spring/cse.bean.xml] org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:317)2022-02-05 22:18:57,488 [INFO] Loading XML bean definitions from URL [jar:file:/C:/Users/n00511724/.m2/repository/org/apache/servicecomb/foundation-vertx/1.2.0.B006/foundation-vertx-1.2.0.B006.jar!/META-INF/spring/cse.bean.xml] org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:317)2022-02-05 22:18:57,521 [INFO] Loading XML bean definitions from URL [jar:file:/C:/Users/n00511724/.m2/repository/org/apache/servicecomb/java-chassis-core/1.2.0.B006/java-chassis-core-1.2.0.B006.jar!/META-INF/spring/cse.bean.xml] org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:317)2022-02-05 22:18:58,137 [INFO] Loading XML bean definitions from URL [jar:file:/C:/Users/n00511724/.m2/repository/org/apache/servicecomb/handler-bizkeeper/1.2.0.B006/handler-bizkeeper-1.2.0.B006.jar!/META-INF/spring/cse.bean.xml] org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:317)2022-02-05 22:18:58,142 [INFO] Loading XML bean definitions from URL [jar:file:/C:/Users/n00511724/.m2/repository/org/apache/servicecomb/handler-loadbalance/1.2.0.B006/handler-loadbalance-1.2.0.B006.jar!/META-INF/spring/cse.bean.xml] org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:317)2022-02-05 22:18:58,142 [INFO] Loading XML bean definitions from URL [jar:file:/C:/Users/n00511724/.m2/repository/com/huawei/paas/cse/cse-handler-cloud-extension/2.3.69/cse-handler-cloud-extension-2.3.69.jar!/META-INF/spring/services.bean.xml] org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:317)2022-02-05 22:18:58,378 [INFO] Environment received, will get configurations from [org.springframework.core.env.StandardEnvironment@1059080630]. org.apache.servicecomb.config.ConfigurationSpringInitializer.setEnvironment(ConfigurationSpringInitializer.java:80)2022-02-05 22:18:58,393 [WARN] No URLs will be polled as dynamic configuration sources. com.netflix.config.sources.URLConfigurationSource.<init>(URLConfigurationSource.java:121)2022-02-05 22:18:58,393 [INFO] To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath. com.netflix.config.sources.URLConfigurationSource.<init>(URLConfigurationSource.java:122)2022-02-05 22:18:58,456 [INFO] create local config: org.apache.servicecomb.config.ConfigUtil.createLocalConfig(ConfigUtil.java:118)2022-02-05 22:18:58,456 [INFO] jar:file:/C:/Users/n00511724/.m2/repository/org/apache/servicecomb/java-chassis-core/1.2.0.B006/java-chassis-core-1.2.0.B006.jar!/microservice.yaml. org.apache.servicecomb.config.ConfigUtil.createLocalConfig(ConfigUtil.java:120)2022-02-05 22:18:58,456 [INFO] jar:file:/C:/Users/n00511724/.m2/repository/com/huawei/paas/cse/cse-solution-service-engine/2.3.69/cse-solution-service-engine-2.3.69.jar!/microservice.yaml. org.apache.servicecomb.config.ConfigUtil.createLocalConfig(ConfigUtil.java:120)2022-02-05 22:18:58,456 [INFO] file:/D:/hcip/Demo-for-microservice/target/classes/microservice.yaml. org.apache.servicecomb.config.ConfigUtil.createLocalConfig(ConfigUtil.java:120)Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.apache.servicecomb.config.ConfigurationSpringInitializer#0' defined in URL [jar:file:/C:/Users/n00511724/.m2/repository/org/apache/servicecomb/foundation-config/1.2.0.B006/foundation-config-1.2.0.B006.jar!/META-INF/spring/cse.bean.xml]: Initialization of bean failed; nested exception is java.lang.RuntimeException: Unable to load Properties2022-02-05 22:18:58,471 [WARN] Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.apache.servicecomb.config.ConfigurationSpringInitializer#0' defined in URL [jar:file:/C:/Users/n00511724/.m2/repository/org/apache/servicecomb/foundation-config/1.2.0.B006/foundation-config-1.2.0.B006.jar!/META-INF/spring/cse.bean.xml]: Initialization of bean failed; nested exception is java.lang.RuntimeException: Unable to load Properties org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:151) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:93) at org.apache.servicecomb.foundation.common.utils.BeanUtils.init(BeanUtils.java:54) at org.apache.servicecomb.foundation.common.utils.BeanUtils.init(BeanUtils.java:47) at helloworld.demo.provider.ProviderMain.main(ProviderMain.java:9)Caused by: java.lang.RuntimeException: Unable to load Properties at com.netflix.config.AbstractPollingScheduler.initialLoad(AbstractPollingScheduler.java:87) at com.netflix.config.AbstractPollingScheduler.startPolling(AbstractPollingScheduler.java:200) at com.netflix.config.DynamicConfiguration.startPolling(DynamicConfiguration.java:60) at com.netflix.config.DynamicConfiguration.<init>(DynamicConfiguration.java:42) at org.apache.servicecomb.config.ConfigUtil.createLocalConfig(ConfigUtil.java:146) at org.apache.servicecomb.config.ConfigUtil.createLocalConfig(ConfigUtil.java:123) at org.apache.servicecomb.config.ConfigUtil.installDynamicConfig(ConfigUtil.java:252) at org.apache.servicecomb.config.ConfigurationSpringInitializer.setEnvironment(ConfigurationSpringInitializer.java:86) at org.springframework.context.support.ApplicationContextAwareProcessor.invokeAwareInterfaces(ApplicationContextAwareProcessor.java:106) at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:97) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:407) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1623) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) ... 13 moreCaused by: java.lang.NullPointerException: Value for property servicecomb.service.registry is null at com.netflix.config.ConcurrentMapConfiguration.addProperty(ConcurrentMapConfiguration.java:178) at com.netflix.config.DynamicPropertyUpdater.addOrChangeProperty(DynamicPropertyUpdater.java:124) at com.netflix.config.AbstractPollingScheduler.populateProperties(AbstractPollingScheduler.java:111) at com.netflix.config.AbstractPollingScheduler.initialLoad(AbstractPollingScheduler.java:85) ... 25 more
-
目录1. 业务架构说明2. 开发环境3. 本地CSE开发环境搭建 3.1. 下载本地CSE 3.2. 解压,双击cse.exe 3.3. 登录CSE控制台4. 接入前准备 4.1. 下载实验工程源码,使用IDEA打开 4.2. IDE 配置 4.3. Maven配置5. 服务端CSE接入 5.1. 主pom修改 5.2. dtse-feign-client接入 5.3. dtse-obs-storage接入 5.4. dtse_system接入 5.5. dtse-zuuL-gateway网关接入6. 前端服务接入7. 其他接入中问题记录 1. 业务架构说明 项目Gitee地址 后端 https://gitee.com/caichunfu/dtse-practice-microservice 前端 https://gitee.com/HuaweiCloudDeveloper/dtse-practice-frontEnd/tree/devCSE改造前: • 微服务包含4个微服务模块:zuul-gateway模块、Eureka注册中心、dtse-system模块、obs-storage模块;其中dtse-system模块、obs-storage模块是业务模块。 • 用户发送请求,微服务网关(zuul-gateway) 过滤器根据请求URI,路由和负载均衡请求到不同服务;同时利用JWT进行token校验请求的合法性。 • Eureka注册中心管理zuul-gateway、dtse-system、obs-storage微服务状态; • dtse-system与obs-storage之间通过feign进行内部接口调用改造技术路径 • 引入使用spring-cloud-huawei • 使用华为云CSE服务替换Eureka注册中心的功能 • 使用Spring Cloud Gateway替换zuul网关基线版本选择 查看 spring-cloud-huawei官网地址: https://github.com/huaweicloud/spring-cloud-huawei 通过实践master分支与openFeign存在兼容问题,所以本次实践以Hoxton为基线版本,Hoxten与openFeign不存在兼容性问题 由于Spring Cloud Huawei与zuul调试中发现有兼容问题,所以将网关替换成Spring Cloud Gateway2. 开发环境JDKOpenjdk 1.8.0_312Maven3.6.3IDEA2021.2.2CSELocal-CSE-2.1.3-windows-amd64.zipspring-boot2.3.5.RELEASEspring-cloudHoxton.SR9spring-cloud-huawei1.8.0-Hoxton3. 本地CSE开发环境搭建3.1. 下载本地CSE https://cse-bucket.obs.cn-north-1.myhuaweicloud.com/LocalCSE/Local-CSE-2.1.3-windows-amd64.zip 3.2. 解压,双击cse.exe3.3. 登录CSE控制台 http://127.0.0.1:30103 4. 接入前准备4.1. 下载实验工程源码,使用IDEA打开 源码地址 https://gitee.com/caichunfu/dtse-practice-microservice 4.2. IDE 配置4.3. Maven配置 使用华为云Maven5. 服务端CSE接入改造5.1. 主pom改造5.1.1. 主pom引入依赖5.1.2. 使用CSE做为注册中心,删除相关模块和依赖 a、删除eureka-server b、引入华为spring cloud` <properties> <java.version>1.8</java.version> <spring-boot.version>2.3.5.RELEASE</spring-boot.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> <spring-cloud-huawei.version>1.8.0-Hoxton</spring-cloud-huawei.version> </properties>`<!-- configure user spring cloud / spring boot versions --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- 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>5.2. dtse-feign-client接入5.2.1. 引入openfeign版本<dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-openfeign-core</artifactId> <version>3.0.3</version> </dependency>5.3. dtse-obs-storage接入5.3.1. Pom文件处理5.3.1.1. 删除zuul依赖 删除spring-cloud-starter-netflix-zuul,增加spring-cloud-starter-netflix-ribbon <!--wmh add--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> <version>2.2.0.RELEASE</version> <scope>compile</scope> <exclusions> <exclusion> <artifactId>ribbon-transport</artifactId> <groupId>com.netflix.ribbon</groupId> </exclusion> <exclusion> <artifactId>rxnetty</artifactId> <groupId>io.reactivex</groupId> </exclusion> </exclusions> <optional>true</optional> </dependency> <!-- wmh delete <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> -->5.3.2. 引入CSE配置5.3.3. 处理com.netflix.client.config.IClientConfig‘ that could not be found问题 出现这种问题是因为启动类找不到(扫描)其他类的路径,处理方法有多种,我这边使用的是方法二 方法一:把启动类放在其他类文件包的同一级,而不要放在上一级 方法二:在启动类的标签中加入启动扫描的路径如下: 方法三: new个IClientConfig类,不过需要初始化,不然会出现空指针5.3.3.1. 方法二指定扫描路径: SpringBootApplication指定扫描路径@SpringBootApplication(scanBasePackages = {"com.huaweicloud.controller","com.huaweicloud.service","com.huaweicloud.commons","com.huaweicloud.persistent"}) @EnableFeignClients @EnableDiscoveryClient public class OBSStorageMain { public static void main(String[] args) { SpringApplication.run(OBSStorageMain.class, args); } }5.3.3.2. 方法三增加config类: IClientConfig 类,重点来了,就是这个类,如果不自己定义(openFeign 是可以自定义这个类的,然后自己初始化),那么就千万不要自己去创建一个 bean 出来,然后自己加上注解定义成配置类如下:@Configuration public class IClientConfig { @Bean public DefaultClientConfigImpl iClientConfig(){ return new DefaultClientConfigImpl(); } } 这玩意千万不要在程序里自己创建出来,可能很多初学者不是很懂,一开始有配置了这个,结果又只是单纯的 return 了一个没有任何属性的 DefaultClientConfigImpl 对象,然后 openFeign 就会使用你创建的这个对象,结果去初始化的时候,就会在源码里面报空指针异常,把这玩意去掉,基本就可以了,如果要自己定义,那记得把里面该有的属性都要初始化值。5.4. Dtse_system接入5.4.1. Pom文件处理 删除eureka client,引入华为service engine <!-- <dependency>--> <!-- <groupId>org.springframework.cloud</groupId>--> <!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>--> <!-- </dependency>--> <dependency> <groupId>com.huaweicloud</groupId> <artifactId>spring-cloud-starter-huawei-service-engine</artifactId> </dependency>删除netflix-hystrix <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>-->5.4.2. 引入CSE配置5.4.3. 处理com.netflix.client.config.IClientConfig‘ that could not be found问题package com.huaweicloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication(scanBasePackages = {"com.huaweicloud.controller","com.huaweicloud.commons","com.huaweicloud.service","com.huaweicloud.persistent"}) @EnableFeignClients //开启feign客户端调用支持 public class SystemMain { public static void main(String[] args) { SpringApplication.run(SystemMain.class, args); } }5.5. Dtse-zuuL-gateway网关接入 使用spring cloud huawei与zuul有兼容性问题,所以切换到Spring Cloud Gateway5.5.1. Pom文件处理 spring-boot-starter-web排除spring-webmvc包,删除spring-cloud-starter-netflix-zuul包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </exclusion> </exclusions> </dependency> 删除eureka-client包 <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>--> 引入spring-cloud-starter-gateway和huawei-service-engine-gateway <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>5.5.2. 引入CSE配置 修改网关配置5.5.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 org.apache.servicecomb.foundation.common.utils.JsonUtils; 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.core.annotation.Order; 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; import java.lang.annotation.Annotation; @Component public class RouteConfiguration implements GlobalFilter, Ordered { @Autowired JwtUtil jwtUtil; @Autowired URIFilter uriFilter; private String writeJson(Object o) { try { return JsonUtils.writeValueAsString(o); } catch (Exception ee) { ee.printStackTrace(); } return null; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("access filter......"); ServerHttpRequest request = exchange.getRequest(); RequestPath path = request.getPath(); System.out.println("收到请求路经:request.getPath() = " + path.value()); // 2、登陆请求放行 if(path.value().contains(uriFilter.getLoginuri().get(0))){ System.out.println("登陆请求路经:request.getPath() = " + path.value()); 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; } }5.5.4. 处理com.netflix.client.config.IClientConfig‘ that could not be found问题package com.huaweicloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class},scanBasePackages = {"com.huaweicloud.config","com.huaweicloud.filter","com.huaweicloud.commons"}) @EnableDiscoveryClient public class SpringCloudGatewayMain { public static void main(String[] args) throws Exception { SpringApplication.run(SpringCloudGatewayMain.class, args); } }6. 前端服务接入 修改vue.config.js,配置服务网关服务的端口 修改login.Vue,通过网关经过systemmain统一接入,所以修改登录url 修改图片上传接口,和获取用户信息接口7. 其他接入中问题记录7.1. 方便openFeign调试,openFeign调试,增加Feign日志类config 增加类FeignConfigurationpackage com.huaweicloud.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfiguration { /** * 日志级别 * * @return */ @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }7.2. Idea 编译报错:Ambiguous method call. Both... IDEA Settings... > Plugins > 搜索已安装的插件Hrisey Plugins > 禁用该插件 7.3. gateway报错org. springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodec Configurer' that could not be found spring-cloud-starter-gateway依赖与mvc是不兼容的,如果要引用spring-boot-starter-web需要把mvc排除 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </exclusion> </exclusions> </dependency>7.4. OpenFeign调用报错com.netflix.client.ClientException: Load balancer does not have available server for client: DTSE-OBS-STORAGE yml 文件里面的服务名,要和 @FeignClient(value = "xxx") 里面的 xxx 一样,切记别弄错,大小写也要一致
-
1.项目Gitee地址https://gitee.com/caichunfu/dtse-practice-microservice2.运行环境JDK1.8Maven3.6.3本地CSE引擎下载地址:https://support.huaweicloud.com/devg-cse/cse_devg_0036.html3.注意事项踩过的一些坑: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-huawei1)使用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.引入依赖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: kie6.启动类添加注解@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: true7.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 会在源码里面使用这个对象,报空指针异常,如果要自己定义,需要初始化里面该有的属性@Configurationpublic 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 Gateway10.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: /login10.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@Slf4jpublic 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.4urifiler配置类@ConfigurationProperties(prefix = "urifiler", ignoreUnknownFields = false)@Data@Componentpublic class URIFilter { private List<String> loginuri;}11.配置中心的使用访问http://127.0.0.1:30103/进入微服务本地引擎管理界面选择配置列表,创建配置项
-
IDC的报告曾指出,到2021年,在超过一半的全球2000强企业中,平均1/3的数字化服务交互都将来自API开放生态系统,增长势头远超其自身的API交互能力。开放的API生态系统是企业数字化平台开放重构的关键。据悉,来自 Twitter、Netflix 和 Google 等公司的广受欢迎的公共 API 平均每天调用 10 亿到50亿次,API调用量逐渐成为一个开放平台成功的关键指标,哪些技术可以驱动API快速增长?拥有12年研发经验的田晓亮给出了他的答案。田晓亮现担任华为云微服务领域首席架构师,他也是国内早期服务网格实践者,曾自研服务网格作为云服务提供给客户业务使用。且听他娓娓道企业数字化转型的关键,以及系统性开展微服务化的一些重要举措。微服务为何如此重要?这里引用Smart Bear 2020年的API报告。如上图所示,微服务技术遥遥领先,成为最关键的领域技术,是企业API经济的技术基石。排在第二的DevOps相关技术则是微服务顺利落地的关键要素和前提。而微服务本身是一种架构模式,这也侧面证明了软件架构设计的价值。一款软件大部分的生命周期都处在维护期,不断地接受新需求,不断变化演进以满足需求。软件架构设计的主要目标便是支撑软件系统的全生命周期,设计良好的架构可以让系统便于理解、易于修改、方便维护,并且能轻松部署。软件架构的终极目标就是最大化程序员的生产力,同时最小化系统的总运营成本,微服务架构恰好能完美地达成这个目标。当然,有个题外话,研发人员也不必担心效率提升自己被优化,因为杰文斯悖论在这里也适用:技术进步提高单位人力利用效率,结果是增加而不是减少企业对人力的需求。是什么阻碍了微服务的应用接下来再看报告中另一个部分,是什么阻碍一个组织应用微服务?依次为:欠缺实践经验和技巧(或者我认为是不易雇佣相关人才);现存代码改造难;工作负担太重,没人(新的需求加入管道,它可以增加营收,此时如果老架构不是业务发展的障碍,就被搁置);IT成熟度不足、流程问题、基础设施问题;没预算;还没确定业务方向;缺少团队协作;缺少工具和技术。因为缺乏上述提到的这些东西,向微服务架构模式前进的企业可谓是千疮百孔。作为云服务提供商,或许可以帮助企业解决这些问题。田晓亮讲了一个他亲身经历的故事:我曾经拜访过一家企业,他们打算让业务团队在云上买些虚拟机自己搭建基础平台,被我阻拦了下来。因为当他们的业务增长后,会遇到各种各样的平台问题,但又没有相关的人才储备。当他们用开源的软件部署,遇到的生产问题其实需要开源社区的人来解决。而这个企业并没有清晰划分职责的技术组织。从商业视角出发,非主营核心业务通过外来购买付费服务来完成更为合适。另外他们也许进入了一个**的架构设计误区,在没有确定业务能力的情况下过早的进行了技术选型,而这个工作通常是要延后处理的,平台的维护成本可能会拖垮业务开发团队。通常情况下,大企业一般会使用云厂商提供的计算、存储等资源,也有自建IaaS服务或者两者并用的,然后在laaS层之上构建PaaS服务,为公司内部的业务服务。所以大型企业会清晰地拆分业务团队与基础设施团队,田晓亮本人就曾是基础设施团队的一员。在这种情况下,基础设施团队也是微服务架构模式的忠实用户。也就是说,他们不但要支撑好平台用户的微服务,自己也应用了该模式。基于多年的微服务转型经验,田晓亮提炼了几个可以帮助企业完成微服务数字化转型的关键点。如何系统性的展开微服务化我们可以围绕软件开发的速度,系统的安全和服务质量来构建微服务相关技术底座。再结合业务的实际发展,逐步转型微服务架构,不必在业务发展初期草草决定技术细节。匹配业务所需是否选择微服务架构模式应该从业务实际需求出发,企业先明确定义业务用例和策略,在此基础上进行架构方案的选择。对于一些刚刚起步的公司,单体应用也许就能满足业务需求。一切以快为准,当单体的开发效率比微服务高时,就果断的选择单体。而随着业务的扩大,企业可以通过绞杀者模式将单体应用向微服务架构模式迁移,进行单体应用绞杀的过程也称为应用现代化的过程。据维基百科,绞杀模式的使用场景是从单体应用平滑演进到微服务,将遗留应用程序转换为现代技术和技术栈的过程,也可称之为应用程序现代化。这种模式通常针对1个遗留应用系统,适合前期野蛮成长,业务增速较快的企业。整个改造流程不需要一步到位,一点点补充新的功能进入管道,比如有的功能可以增加营收,此时如果老架构不是企业业务发展的障碍,就可以被搁置并随时重启演进。具体策略有:新功能用新的微服务实现(阻止单体水平扩展和演进)隔离表示层和后端(降解单体)将现有的功能提取到微服务或者独立为微服务(降解单体)华为云CSE,解决分布式系统治理之难从单体成为微服务之后,假如没有成熟的工具配套,一切的成本都是翻倍提升的,这也是为什么说技术没有银弹。运行时的治理就是其中一个难题。华为云CSE的设计意图就是让企业的治理成本和门槛降低,以保障业务稳定健壮的运行,提供高质量API服务。CSE微服务引擎提供了基于业务视角的流量治理功能,使用可视化界面规划流量策略,无需关心微服务系统的复杂性。企业需要的只是定义好流量特征,比如登陆操作每分钟只能允许5次。另外使用Spring Cloud编程的应用可以无缝使用云上的能力,无需额外编码。CSE还提供了RBAC认证鉴权系统,提供基于标签的细粒度资源授权功能,可以规划多个账号,赋予角色和相关权限,保证多个团队协作过程中不会误操作或者访问不该访问的数据。这套认证系**立于华为云的IAM,类似MySQL的账号系统是完全独立的,这样团队之间就可以安全共享一套微服务引擎。微服务编程框架or服务网格,业务为王在进行编程框架和服务网格(Istio)之间的技术决策前,不妨先遵循架构设计阶段一个较好的指导思想——保留可选项,即尽可能推迟技术细节的相关决策。比如先确定业务能力是什么,收集用例,再决定选关系型数据库还是非关系型数据库,亦或是两者皆用。任何一种技术的选择皆如此。如在Spring Cloud这类编程框架和服务网格之间的选型存在疑虑时,就要充分的分析企业的业务是否匹配这些技术的适用场景,是否能支撑商业成果。因为选择什么技术手段支撑,终究要回归到业务本质来。企业要对多种可彼此替代的技术进行详尽的洞察和对比,结合需要支撑的业务场景做详尽的论证来验证最适合的技术到底是什么。下边这张表格列举了微服务开发下这两种技术方案的部分特性对比,它并不代表孰优孰劣,只是告诉企业要选择对应的能力来匹配业务、支撑业务。打个比方,Istio能提供绝对的内部网络通信安全,这是它独有的能力,如果企业的业务需要这样级别的防护,那么选择Istio管理会更加方便,而Istio却有很多不关心的维度,而这些维度正式编程框架提供的能力。管理好API如果API主要给外部系统调用,最好采用顶层设计的策略。不建议把这些对外暴露的API的设计权放到各个团队手中,因为最外层的、系统平台消费者可见的API必须是定义良好,风格一致的API,并且严格控制兼容性,可以使用CloudTest对这些API编写测试用例,并且在每次部署后,都执行一遍测试用例以确保平台的行为始终一致。API是业务的门面,可以采用Spring Cloud Gateway开发框架编写对外呈现的API,框架会自动生成Open API文档并注册到CSE微服务引擎实例中,这样可以重点审视API的定义,及时进行改进,高质量的交付所有的API。从DevOps文化建设入手当企业全面推动DevOps文化,并熟练让交付团队掌握相关工具后,微服务架构思维必然会在组织里萌芽。团队需要代码库,流水线等一系列服务来打造一条高效的生产线,故此DevOps技术因为其重要性排在了关键技术第二位。如果没有DevOps的成熟建设,就不要展开架构微服务化。华为云的DevCloud是集华为研发实践、前沿研发理念、先进研发工具为一体的研发云平台,面向开发者提供研发工具服务,让软件开发简单高效当然,系统性展开微服务化并非只有技术类工作,文化理念、团队合作形式、关键成员不断学习和精进都是微服务化的关键。总结简而言之,微服务架构满足了架构设计模式中的单一职责和共同闭包原则,它是业务和商业发展成规模化后的必经之路。微服务既符合架构设计原则,也能最小化系统运营成本,支撑高效的商业活动,在激烈的竞争中起到了“润滑剂”的作用。所以,你的业务是否已经准备好应用微服务了?
-
2022年1月13日,中国保险行业协会(以下简称“保险业协会”)在京发布《保险行业基于容器的云计算平台架构》《面向保险行业的微服务架构技术能力要求》《保险行业基于容器的云计算平台成熟度模型》《保险行业应用开发的微服务架构成熟度模型》四项保险行业云计算相关标准(以下简称“四项标准”)。 保险业协会有关负责人表示,四项标准地制定是基于保险业云计算、容器、微服务的实际落地需求,推动保险业与云计算的跨界融合,科学有效地对保险业云计算市场进行规范,引导科技支撑保险业务发展。 近年来,保险业与云计算的融合逐步加深,众多保险机构积极部署企业上云实践。在当前金融科技重构保险业态的阶段,更多的新兴技术融入保险领域中改变行业业态,发展云计算也成为保险企业实现数字化转型及科技驱动的关键。 据了解,四项标准聚焦于容器和微服务。其中《保险行业基于容器的云计算平台架构》《保险行业基于容器的云计算平台成熟度模型》这两项容器相关标准旨在衡量保险企业容器技术发展水平,推动容器技术的快速部署、轻量灵活等特性在保险行业的深入应用。《面向保险行业的微服务架构技术能力要求》《保险行业应用开发的微服务架构成熟度模型》这两项微服务相关标准则旨在衡量保险企业微服务技术发展水平,推动微服务技术快速弹性扩容缩容、高可用等特性在保险行业的深入应用。 作为云计算领域的主流技术,容器和微服务正日益成为保险企业的应用重点,四项标准填补了当前保险业在这两个技术领域的标准空白。保险业协会有关负责人表示,四项标准的发布和实施,将对保险业采用云计算、容器以及微服务技术产生积极影响。通过新技术对业务流程进行整合优化,为保险业的业务运营模式以及风险监管模式开创新局面,对保险业的客户体验、运营效率以及风险管控能力进行提升。 据悉,四项标准由中国信息通信研究院和太平洋(3.260, -0.07, -2.10%)保险牵头,人保财险、国寿寿险、中再集团、华为、腾讯等十余家公司共同参与了编制,整个编制工作历时近两年,期间经过多次深入调研、研讨和论证,收集吸收保险行业及信息通信行业一百余条反馈意见,最终完成了四项标准制定工作。
-
Local CSE是华为云微服务提供给广大开发者的本地轻量化微服务引擎,用于本地开发的轻量服务中心、配置中心,并提供简单的界面,开发者可以通过Local CSE零改造快速接入部署微服务,无需付费即可进行微服务开发,是广大开发者的微服务开发利器 如何简单接入Local CSE,零成本学习轻量化微服务引擎?如何多场景巧妙运用Local CSE?看华为云微服务高级工程师为开发者详解 Local CSE的服务注册与配置中心,手把手Local CSE实践教学…2022年开年直播微服务本地轻量化引擎 Local CSE利剑出鞘<直播时间>2月17日 19:00-20:00<直播嘉宾>昱霖 华为云微服务框架高级工程师Echo 华为云微服务产品经理<直播地址>微服务快速开发利器之 Local CSE直播预热活动,多重好礼等你来>>戳我,参与直播预热赢好礼<<好礼一:报名送码豆活动期间,成功报名直播并签到可获得200码豆。(限前500名)好礼二:参与邀请排行榜赢大奖邀请好友报名直播并签到赢荣耀Xsports运动蓝牙耳机/华为智能体脂秤 3 /华为mini蓝牙音箱等好礼注意:①人数相同时,以最先到达的为准;②若排名TOP1未达到最低有效邀请数量,则奖励顺延,例如:张三有效邀请量为799人,排名TOP1,则张三的奖励为华为智能体脂秤 3。邀请好友的具体操作流程如下:步骤一:邀请人成功报名活动后(点击去报名),点击图示中的“分享有礼”按钮 步骤二:通过生成的专属链接/邀请二维码,即可邀请好友报名活动。步骤三:好友报名直播后,点击“进入直播间”,填写签到问卷。有效邀请的定义(邀请的好友):所有被邀请人员需满足以下条件才可算成功邀请,纳入奖励数量:拥有华为云账号;2.回到直播间填写签到问卷。看直播,参与提问互动,奖品抽抽抽!1. 直播提问直播期间在“问答区”向专家提问,专家抽中问题进行答疑,抽中问题即可获得《持续演进的CloudNtive 云原生架构下微服务最佳实践]》书籍1本或华为云定制桌面鼠标垫1个(提问格式:手机号码后四位+问题,例:2333+如何零成本学习轻量化微服务引擎?)2. 直播抽奖直播期间,根据小助手在直播间提示的关键词口令进行抽奖,中奖即可获得《持续演进的CloudNtive 云原生架构下微服务最佳实践]》书籍1本或华为云定制桌面鼠标垫1个 [持续演进的CloudNtive 云原生架构下微服务最佳实践] [华为云定制桌面鼠标垫]更多好礼,更多精彩干货,尽在微服务直播间!2022年2月17日(周四)19:00-20:00我在直播间等你>> 扫码加入直播交流群 <<温馨提示:获奖用户将在2月24日16点前公布至本活动帖,请获奖用户在2022年3月3日15点前联系小助手(微信号:servicestage02)完善邮寄地址,过期未联系者视为自动放弃获奖奖品。活动奖品颜色随机,不接受指定,实物礼品将在活动结束后15个工作日内发货,码豆礼品将在活动结束后5个工作日内发放。本次活动一个实名认证账号只能对应一个获奖人,如同一账号填写多个不同获奖人,不予发放奖励;活动参与需遵守《华为社区常规活动规则》;华为云微服务拥有活动最终解释权。 华为云码豆怎么用?会员中心入口:https://devcloud.huaweicloud.com/bonususer/home码豆奖励活动规则:1)码豆可在码豆会员中心兑换实物礼品;2)码豆奖励将于活动结束后的3个工作日内充值到账,请到会员中心的“查看明细”中查看到账情况;3)码豆只能用于会员中心的礼品兑换,不得转让,具体规则请到会员中心阅读“码豆规则”;4)为保证码豆成功发放,如果修改过账号名还请向工作人员提供修改前后的账号名。
-
>摘要:云原生浪潮下,容器技术是串联起整个云原生世界的关键一环。本文分享自华为云社区[《左手自研,右手开源,技术揭秘华为云如何领跑容器市场》](https://bbs.huaweicloud.com/blogs/281793?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=other&utm_content=content),作者:华为云社区精选。  近日,IDC 发布的《PRC SDC Market Overview and Analysis, 2020H2/2020》报告显示,华为云**以24.3%的市场份额,斩获中国容器软件市场第一**。 下面,我们从技术角度分析,华为云为什么能领跑容器软件市场。 # 容器如何成为宠儿? 容器是什么? 从字面上看,这是一个用于盛放某种东西的器具,实际也是如此,容器技术可以将软件的程序代码和依赖项打包起来,让其与实际运行的环境隔离,哪里需要搬哪里,比如在数据中心、个人电脑上部署运行。 这个概念有点像老大哥虚拟机,但是两者的相似点仅仅在于:提供独立的环境运行软件。在[基因、容器和上帝](https://bbs.huaweicloud.com/blogs/114535) 中,作者从哲学化的视角谈了程序员创造的虚拟世界,也点出了两者的异曲同工之妙:Docker容器技术和VM虚拟机从技术原理上看,是完全不同的路线,连实现思路都不一样。但是,它们所达成的效果或者说是目标确是惊人的一致:即模拟一台看着像物理机一样的东西。 虽然如此,但两者内在逻辑差别很大。容器可以在操作系统级别进行虚拟化,一个操作系统内核上可以运行多个容器,而虚拟机只是硬件层面的虚拟化。相比较VM,容器更轻巧、启动速度更快、占用的内存微乎其微,[容器与Docker](https://bbs.huaweicloud.com/blogs/203431)详细对比了虚拟机和容器的优缺点。 随着用户对云端应用开发、部署和运维的效率愈加重视,间接促成了容器的盛行。 不过,容器在云服务领域“发光发热“离不开一个关键技术:**Docker**。  Docker是目前应用最多的容器引擎技术,它的logo是一只蓝色的鲸鱼驮着一堆小方块。开发者通过docker可以为任何应用创建容器:应用的流程、库和依赖,甚至整个操作系统的文件系统能被打包成一个简单的可移植的包,这个包就像是鲸鱼背上蓝色的小方块,它可以在任何运行Docker的机器上使用,从根本上解决了开发运行环境不一致的问题,让容器真正实现了一次构建,随处运行。 当应用程序被分解为多个小组件或服务,每个组件或服务都放置在一个容器中,每个容器可能还运行在不同的计算机中,此时就需要对容器进行有序的编排和管理。就像电脑上的操作系统,它可以管理所有应用程序,并规划哪个应用程序何时使用电脑的CPU和其他硬件资源。 脱胎于Google内部集群管理系统Borg的kubernetes逐渐成为业界标准,它可以通过API的方式将多个不同的计算机作为一个资源池进行管理,类似某种集群操作系统,管理整个集群中的容器化应用程序。[ Docker与Kubernetes的兴起](https://bbs.huaweicloud.com/blogs/138949)这篇文章就具体谈到了kubernetes(k8s)如何从三足鼎立的局面中PK掉其他两个对手,在混战中取得胜利。 至此,属于容器的黄金时代大幕正式拉开。 Gartner预测,到2023年,70%的组织将在生产中运行三个或更多容器化应用程序。容器、Kubernetes和微服务应用模式是企业IT创新和数字化转型的三大驱动力。 华为很早就投入了容器的怀抱中,由于一直使用虚拟机封装应用程序,每次启动虚拟机花费了大量的时间,这给管理及部署基于虚机应用程序的高成本和低效率带来了挑战。所以在2015年的时候,华为决定利用Kubernetes技术对自身IT系统进行容器化改造。华为在通过自身的容器化改造实践受益的同时,又将遇到的实际问题不断贡献给社区,与社区成员一同推动Kubernetes的发展。 2018年4月,华为云获得了CNCF基金会的顶级席位——CNCF技术监督委员会席位,全球共9席,华为是亚洲首家进入者。 同时,随着越来越多的企业业务选择容器化,在集群的规模、性能、实时监控与弹性扩缩容等方面都提出了新的要求,当开源社区方案难以解决这些问题的时候,就非常考验各大云服务供应商的技术能力。 # 万丈高楼平地起,建设好云原生基础设施 在[为什么说容器的崛起预示着云原生时代到来?](https://bbs.huaweicloud.com/blogs/200395)中,华为云云原生团队认为,各类现代化的应用都将会运行在K8s之上,不仅仅是当前以互联网App、WebService为代表的无状态应用,还有新型的诸如大数据、AI、分布式数据中间件等等有状态应用,以及边缘应用也将会普遍运行在K8s之上,K8s将完成对各类现有平台的归一化,成为一个统一的应用运行的基础平台。 华为云最早于2018年洞察到了这一技术趋势,在容器全栈产品中构建了Vessel云原生技术平台,主要包括了以**容器引擎iSula、容器网络Yangtse、容器存储Everest**为代表的面向统一资源层的云原生基础设施组件。  下面,我们将逐一为大家揭开华为云的容器技术面纱。 # 容器引擎iSula 基于docker和kubernetes,首先Docker并不是万能药,它在某些场景下也存在不足,比如: - 在资源敏感环境,或需要部署高密度容器节点时,容器对基础设施的资源占用会急剧升高; - 当大规模应用拉起或遇到突发流量时,并发速度可能成为瓶颈。 当主流的 Docker 等容器引擎在特定用例下力不从心时,一些针对某种用例进行过专门优化的容器引擎技术开始崛起。比如说,以 Kata Container 为代表的专门针对容器隔离性不够严格而设计的安全容器技术;以 iSula 为代表的针对资源受限的边缘计算和 IoT 环境设计的轻量级容器技术。 可以看出,iSula是与Docker相对的一种容器引擎,它一方面完全兼容现有容器生态,另一方面相比Docker内存占用下降68%、启动时间缩短35%。 比如相比Golang编写的Docker,iSula使用C/C++实现,具有轻、灵、巧、快的特点,不受硬件规格和架构的限制,底噪开销更小。在严苛的资源要求环境下,轻量模式下的 iSulad 本身占用资源极低( 15M) 。  2017 年,iSula 技术团队成功将 Kata Containers 集成到 iSula 容器平台,并于 18 年初应用于华为云容器服务,**推出基于iSulad 容器引擎和 Kata Containers 的商用容器服务——华为云容器实例 CCI(Cloud Container Instance),这也是业界首个 Serverless 架构的云容器服务**。 那么,基于iSulad 容器引擎和 Kata Containers 如何打造安全、高性能的CCI?且看 [基于 Kata Containers 与 iSulad 的云容器实践解析](https://bbs.huaweicloud.com/blogs/147030)进一步分析,文中提到真正的 Serverless 容器服务中,集群管理由云服务提供商承担,客户只需要关注每个应用的容器实例即可。在这种情况下,云服务提供商需要考虑如何在统一管理面下保证每个用户的安全。 CCI 服务所属的 Kubernetes 集群直接部署在裸金属服务器之上,底层是 Kata Containers,中间靠 iSula 容器平台连接。其中,依靠 Kata Containers 的强隔离特性,多个租户之间的容器运行环境强隔离,不同租户之间的容器不感知、不可见,做到在同一台裸金属服务器上混合部署而安全无虞。 安全之外,在算力方面,CCI基于iSula提供的GPU直通功能,可以直接在容器中使用各种GPU进行AI计算。再加上CCI无需购买和管理弹性服务器,可直接在华为云上运行容器和pod,也无需创建集群,管理master和work节点,非常适用于批量计算,高性能计算,突发扩容,以及CI/CD测试。 在此,华为云社区推荐一些有趣的案例,可以帮助大家快速上手CCI,比如[云容器实例CCI - 使用Tensorflow训练神经网络](https://support.huaweicloud.com/bestpractice-cci/cci_04_0008.html#toTop) 和[云容器实例CCI – 经典2048数字合成游戏部署指南](https://bbs.huaweicloud.com/forum/thread-17431-1-1.html) ,通过这些简单的实操和小游戏,能够对Serverless 架构的云容器服务有更直观的认识。 # 容器网络Yangtse 大家应该都看过某些明星导致社交媒体平台宕机的新闻,明星事件带来的突发流量触发业务扩容,以前是扩容虚拟机,速度慢还情有可原,现在大部分互联网平台都使用容器了,为什么扩容速度还是跟不上流量增长的节奏呢? 首先,Kubernetes本身并不负责网络通信,它提供了容器网络接口CNI负责具体的网络通信,开源的CNI插件非常多,像Flannel、Calico等。包括华为云容器引擎CCE也专门为Kubernetes定制了CNI插件,使得Kubernetes可以使用华为云VPC网络。 尽管如此,多个容器集群的网络通信(容器连接到其他容器、主机和外部网络的机制)始终是个复杂的问题。比如大规模节点管理场景下网络性能的瓶颈;网口发放速度如何匹配容器扩容速度等等。最终,对对底层虚拟化网络提出了**密度更高,规模更大,发放更快,调整更频繁**的要求。 容器网络Yangtse深度融合华为云虚拟私有云(VPC)原生网络能力,它采用的VPC-Native组网被称作ENI(Elastic Network Interface)模式,容器直接挂载具有VPC子网地址的ENI,具备完全VPC网络互通能力。容器实例可以在集群VPC网络和与之相连的其他VPC网络中进行原生路由,并且直接使用VPC原生的网络能力如 network policy、ELB、EIP、NAT等。换言之,就是容器地址属于VPC子网,容器独占对应的ENI网口,解决了容器的互通性问题。 而且Yangtse基于华为云擎天架构的软硬协同能力,会把治理和转发逻辑下沉到擎天卡上,实现容器网络主机资源0占用。数据显示,通过硬件直通方式及动态网络队列,网络整体性能提升40%,单容器PPS提升2倍;基于warm pool的能力,1-2秒内完成ENI的发放和网络端到端打通。 至于具体如何实现,大体上可以总结为三点: 1、warm pool 机制可以解决网络资源预热的问题。如果不做预热,容器网络端到端打通时间在一分钟以上,分钟级容器启动时间是不可接受的。 Warm pool机制在裸金属节点上预挂载一定数量ENI(用户可根据服务部署并发量自定义配置),容器随时调度到预热节点上都有即时可用的ENI网卡。经过warm pool的优化,容器网络端到端打通时间缩短为1s-2s。 2、得益于擎天架构的优势,裸金属容器还可以向虚拟机容器扩容,而在虚拟机容器上,容器网络Yangtse使用了Trunkport技术,结合ENI的优势,在保障性能的前提下,单台服务器理论上可为千容器同时提供直通网络能力。 3、当应用业务流量增长触发扩容时,如果ELB直接全量发放分摊的流量请求,海量请求会迅速压垮(overload)新扩的容器,造成扩容失败。 所以新扩容的后端实例需要“慢启动”的过程,但一个节点部署多个容器时,节点的二次分发让ELB无法感知到最终的后端容器,进而无法做到容器级别的流控,也难以保证稳态后的负载均衡。容器网络Yangtse实现了与华为云ELB v3独享型负载均衡实例的直通。 具体技术详解,可以阅读[华为云第二代裸金属容器技术系列:应对海量并发的网络黑科技](https://bbs.huaweicloud.com/blogs/194532) 。 目前,Yangtse已经为华为云CCE/CCI/IEF等容器服务提供了统一的容器网络方案,覆盖虚机、裸金属、Serverless和边缘节点等各种容器运行环境。 其中**最值得注意的是CCE,它是一种托管的Kubernetes服务,可进一步简化基于容器的应用程序部署和管理,深度整合华为云的计算、存储、网络和安全等技术构建高可用Kubernetes集群**。 在CCE中,用户可以直接使用华为云高性能的弹性云服务器、裸金属服务器、GPU加速云服务器等多种异构基础设施,也可以根据业务需要在云容器引擎中快速创建CCE集群、鲲鹏集群、CCE Turbo集群,并通过云容器引擎对创建的集群进行统一管理。 以今年在HDC重磅发布的云容器集群CCE Turbo为例,它主要针对企业大规模业务生产中的高性能、低成本诉求,在计算、网络和调度上全方位加速, [新一代容器解决方案:云容器引擎CCE Turbo集群](https://bbs.huaweicloud.com/blogs/198569)就总结了它在这三个方面的新突破。 >在计算加速方面,业界独家实现容器100%卸载,服务器资源和性能双零损耗。 >在网络加速方面,采用独创的容器直通网络,让两层网络变成一层,端到端连通时间缩短一半,有效支撑业务秒级扩容千容器。 >在调度加速方面,通过感知AI、大数据、WEB业务的不同特征,以及应用模型、网络拓扑等,实现业务混合部署、智能调度,还自动优化任务调度策略,实现1万容器/秒的大规模并发调度能力。 再就是容器存储Everest, 每个POD使用独立VF,读写时延降低50%;将Posix组件卸载,单进程节省30M内存;NAS卷直挂POD容器内,提高请求处理效率30%。 # “查漏补缺”Kuberentes,开源技术解决特殊场景难题 基础设施之外,华为云先后将Vessel的核心组件Volcano和KubeEdge开源,并贡献给云原生计算基金会CNCF,成为社区首个容器智能边缘项目和容器批量计算项目。 # Volcano——批量计算 当有更多的用户希望在Kubernetes上运行大数据、 AI和HPC应用,而它默认调度器又无法满足包括公平调度、优先级、队列等高级调度功能时,就需要一些新的技术解决方案登场了。 考虑到AI、大数据等业务的需求,华为云在Kubernetes调度上做了一个感知上层业务的调度——Volavano,它是基于Kubernetes构建的一个通用批量计算系统,[Volcano架构设计与原理解读](https://bbs.huaweicloud.com/blogs/239645)就Volcano产生的背景、架构设计与原理进行深度解读,用数据证明了Volavano为分布式训练、大数据、HPC场景带来了效率的提高。 [Volcano火山:容器与批量计算的碰撞](https://bbs.huaweicloud.com/blogs/205045)则从并行计算开始说起,详细解释了Volcano的调度框架、调度实现原理。作为调度系统,Volcano通过**作业级的调度**和**多种插件机制**来支持多种作业,其中作业级的调度支持以多种类型的作业为目标进行设计,比如基于时间的、跨队列的等等。Volcano的插件机制有效的支撑了针对不同场景算法的落地,从早期的gang-scheduling/co-scheduling,到后来各个级别的公平调度。  图:总体架构 在华为云今年刚推出的CCE Turbo容器集群中,就采取了多项Volcano关键调度技术,如基于共享资源视图的多调度器、决策复用、应用模型感知、数据位置亲和调度、网络拓扑调度等,从而实现1万容器/秒的大规模并发调度能力。 # KubeEdge——边缘计算 容器天然的轻量化和可移植性,非常适合边缘计算的场景。理想情况下,在边缘部署复杂的应用,Kubernetes 是个很好的选择,现实真相是要想在边缘部署 Kubernetes集群,各种问题层出。 比如很多设备边缘的资源规格有限,特别是 CPU 处理能力较弱,因此无法部署完整的 Kubernetes;Kubernetes 依赖 list/watch 机制,不支持离线运行,而边缘节点的离线又是常态,例如:设备休眠重启;边缘业务通常在私有网络中,无公网IP,云边跨越公网导致延迟高。 为了解决这些问题,KubeEdge应运而生。KubeEdge即Kube+Edge,顾名思义就是依托K8S的容器编排和调度能力,实现云边协同、计算下沉、海量设备的平滑接入。 其架构主要包含三部分,分别是云端、边缘侧和终端。云端负责应用和配置的下发,边缘侧则负责运行边缘应用和管理接入设备。 [KubeEdge架构解读:云原生的边缘计算平台](https://bbs.huaweicloud.com/blogs/241350)从KubeEdge架构设计理念、KubeEdge代码目录概览、KubeEdge集群部署三方面带大家认识KubeEdge。  关于KubeEdge和Volcano的更多技术硬实力体现和落地案例,可以阅读专题[【技术补给站】第5期:从架构和实践,剖析KubeEdge+Volcano技术硬实力](https://bbs.huaweicloud.com/blogs/240234),在此不再赘述。 # 解决多云容器集群管理,新秀Karmada崛起 批量计算和边缘计算的问题解决后,伴随云原生技术和市场的不断成熟,很多企业都是多云或者混合云的部署,一方面可以避免被单供应商锁定降低风险,另一方面也可以是出于成本的考量。 但是多集群同时也带来了巨大的复杂性,包括如何让应用面向多集群部署分发,并做到多集群之间灵活切换。在今年的HDC上,华为云宣布了**多云容器编排项目Karmada正式开源**,Karmada项目由华为、工商银行、小红书、中国一汽等8家企业联合发起,它可以构建无极可扩展的容器资源池,让开发者像使用单个K8s集群一样使用多云集群。 Karmada是一个 Kubernetes 管理系统,基于 [Kubernetes Federation v1](https://github.com/kubernetes-retired/federation) 和[ v2](https://github.com/kubernetes-sigs/kubefed) 开发,它可以跨多个 Kubernetes 集群和云运行云原生应用程序,直接使用 Kubernetes 原生 API 并提供高级调度功能,实现真正的开放式多云 Kubernetes。 [华为云MCP多云跨云的容器治理与实践](https://bbs.huaweicloud.com/blogs/281796)为我们梳理了Karmada项目诞生的前因后果,以及整个项目的核心价值,比如对K8s原生API兼容 、丰富的多集群调度支持、开箱即用等等。  Karmada的架构设计图 从架构图可以看到,整个控制面板可以分为四大块:提供原生API入口,存放用户yaml和Karmada资源对象的Karmda API Server,和配套存储ETCD;以及资源控制器Karmda Controller Manager、多集群调度器 Karmda Sheduler。其中,最关键的就是API Server,让用户既有的资源配置(yaml)可以借助K8s原生API进行部署。 综上,Karmada 旨在为多云和混合云场景下的多集群应用程序管理提供 turnkey 自动化,其关键功能包括集中式多云管理、高可用性、故障恢复和流量调度。 今年的HDC期间,在线教育平台VIPKID的后端研发高级专家分享了使用 Karmada实现从天到秒的跨云迁移实践。在剖析VIPKID的多场景云原生落地实践后,他对多集群管理提出了一些思考,如下图所示,理想的多集群管理方式是: - 集中管理,但要原生; - 应用在不同集群的差异化管理; - 集群故障自动转移。  对比了多家方案后,VIPKID选择了开源的方案 Karmada。作者表示Karmada的整个设计结构就是按原生k8s开发标准开发的,唯一差别之处是需要管理多个集群不同的 workload信息,所以改写了调度器和控制器,在它们下面对接了多个集群管理起来。这样最大的好处是看起来在控制一个集群,但最终的效果是在控制多个集群。 具体案例情况参见[Karmada | VIPKID在线教育平台从天到秒的跨云迁移实践](https://bbs.huaweicloud.com/blogs/281788),文章内有现场demo演示用Karmada实现多集群管理。 另一个经典案例是[工商银行多k8s集群管理及容灾实践](https://bbs.huaweicloud.com/blogs/281790)。工商银行的应用平台云容器规模超20万,业务容器占到55,000个左右,整体的一些核心业务都已入容器云内部,包括个人金融体系的账户,快捷支付、线上渠道等。当越来越多的核心业务应用入云之后,最大的挑战是容灾以及高可用。 但既有的运管平台并不能解决这些问题,比如没有整体的跨集群自动伸缩能力、集群对上层用户不透明、没有跨集群的自动调度和迁移等等。对此,他们根据业务场景调研了一些多集群管理平台,最终也选择了社区支持的开源项目Karmada。 Karmada以类k8s的形式部署,对他们来说,改造成本是比较小的,只需在上面部署一个管理集群即可。而且Karmada仅管理资源在集群间的调度,子集群内分配高度自治。在实践中,Karmada的资源调度、容灾、集群管理和资源管理的优势突出。 截止到现在,工行在测试环境中已经用Karmada对存量集群进行一些纳管,未来规划的关键的点是如何和整体云平台进行集成。 项目地址 :GitHub - karmada-io/karmada: Open, Multi-Cloud, Multi-Cluster Kubernetes Orchestration 总结 围绕Docker和kubernetes,华为云在技术层面做了诸多的优化和新的探索尝试,除此之外,华为云还有端到端的容器运维管理体系,涵盖资源编排、容器应用持续交付、应用生命周期管理、镜像安全扫描以及日常运维监控等。 云原生浪潮下,容器技术是串联起整个云原生世界的关键一环,它的市场之争,正是山雨欲来风满楼,想要占得高地,技术实力、开源生态、合作伙伴,缺一不可。
-
Local CSE是华为云微服务提供给广大开发者的本地轻量化微服务引擎,用于本地开发的轻量服务中心、配置中心,并提供简单的界面,开发者可以通过Local CSE零改造快速接入部署微服务,无需付费即可进行微服务开发,是广大开发者的微服务开发利器 如何简单接入Local CSE,零成本学习轻量化微服务引擎?如何多场景巧妙运用Local CSE?看华为云微服务高级工程师为开发者详解 Local CSE的服务注册与配置中心,手把手Local CSE实践教学…2022年开年直播微服务本地轻量化引擎 Local CSE利剑出鞘<直播时间>2月17日 19:00-20:00<直播嘉宾>昱霖 华为云微服务框架高级工程师Echo 华为云微服务产品经理<直播地址>微服务快速开发利器之 Local CSE直播预热活动,多重好礼等你来>>戳我,参与直播预热赢好礼<<好礼一:报名送码豆活动期间,成功报名直播并签到可获得200码豆。(限前500名)好礼二:参与邀请排行榜赢大奖邀请好友报名直播并签到赢荣耀Xsports运动蓝牙耳机/华为智能体脂秤 3 /华为mini蓝牙音箱等好礼注意:①人数相同时,以最先到达的为准;②若排名TOP1未达到最低有效邀请数量,则奖励顺延,例如:张三有效邀请量为799人,排名TOP1,则张三的奖励为华为智能体脂秤 3。邀请好友的具体操作流程如下:步骤一:邀请人成功报名活动后(点击去报名),点击图示中的“分享有礼”按钮 步骤二:通过生成的专属链接/邀请二维码,即可邀请好友报名活动。步骤三:好友报名直播后,点击“进入直播间”,填写签到问卷。有效邀请的定义(邀请的好友):所有被邀请人员需满足以下条件才可算成功邀请,纳入奖励数量:拥有华为云账号;2.回到直播间填写签到问卷。看直播,参与提问互动,奖品抽抽抽!1. 直播提问直播期间在“问答区”向专家提问,专家抽中问题进行答疑,抽中问题即可获得《持续演进的CloudNtive 云原生架构下微服务最佳实践]》书籍1本或华为云定制桌面鼠标垫1个(提问格式:手机号码后四位+问题,例:2333+如何零成本学习轻量化微服务引擎?)2. 直播抽奖直播期间,根据小助手在直播间提示的关键词口令进行抽奖,中奖即可获得《持续演进的CloudNtive 云原生架构下微服务最佳实践]》书籍1本或华为云定制桌面鼠标垫1个 [持续演进的CloudNtive 云原生架构下微服务最佳实践] [华为云定制桌面鼠标垫]更多好礼,更多精彩干货,尽在微服务直播间!2022年2月17日(周四)19:00-20:00我在直播间等你>> 扫码加入直播交流群 <<直播中奖名单公示一、直播预热活动:直播报名签到中奖用户名单如下,奖励已发放到账,请查收核对账号名码豆奖励hid****s200wk5****8200hw7****5200can****n200zhe****u200hw3****9200yiz****l200cai****y200hw4****5200hid****_200kua****y200lon****2200z11****5200hw4****4200hid****7200hw0****7200cao****8200hw1****6200a37****7200xj1****1200cft****7200hw_****1200hen****c200zzz****i200yc-****n200hw7****1200mee****0200fuh****a200nuk****n200hw5****3200qin****6200hw4****1200bru****0200hw1****3200shy****s200温馨提示:获奖用户将在2月24日16点前公布至本活动帖,请获奖用户在2022年3月3日15点前联系小助手(微信号:servicestage02)完善邮寄地址,过期未联系者视为自动放弃获奖奖品。活动奖品颜色随机,不接受指定,实物礼品将在活动结束后15个工作日内发货,码豆礼品将在活动结束后5个工作日内发放。本次活动一个实名认证账号只能对应一个获奖人,如同一账号填写多个不同获奖人,不予发放奖励;活动参与需遵守《华为社区常规活动规则》;华为云微服务拥有活动最终解释权。 华为云码豆怎么用?会员中心入口:https://devcloud.huaweicloud.com/bonususer/home码豆奖励活动规则:1)码豆可在码豆会员中心兑换实物礼品;2)码豆奖励将于活动结束后的3个工作日内充值到账,请到会员中心的“查看明细”中查看到账情况;3)码豆只能用于会员中心的礼品兑换,不得转让,具体规则请到会员中心阅读“码豆规则”;4)为保证码豆成功发放,如果修改过账号名还请向工作人员提供修改前后的账号名。
-
急急急,求助!!!!! java实习生第一次接触Service combService comb,自己在本服务写代码,自测通过。结果到项目测试,其他服务调用出错,比较着急,网上没有解决办法,公司说是:因为发的xml报文 应该是框架 把这个报文转义了,你在这里想办法 把报文转义的去掉 就可以了,直接通过Http调用就不会有这个问题的,主要是用了CSE。1、这是调用过程2:debug发送的string类型的xml报文3.接受方 接受的是String类型的xml报文,就在这产生错误,多了""。4:从这里看,明显接受方的xml报文解析的不正确,多了首尾""。不知道有没有大佬了解这的原因,跪求讲解。
-
>摘要:Docker为什么火,靠的就是Docker镜像。他打包了应用程序的所有依赖,彻底解决了环境的一致性问题,重新定义了软件的交付方式,提高了生产效率。本文分享自华为云社区[《认识容器,我们从它的历史开始聊起》](https://bbs.huaweicloud.com/blogs/285728?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=other&utm_content=content),作者:技术火炬手。 关于容器的历史、发展以及技术本质,在互联网上已经有非常多的文章了。这里旨在结合自身的工作经验和理解,通过一系列的文章,讲清楚这项技术。 # 容器的历史和发展 ### 1、前世 讲到容器,就不得不提LXC(Linux Container),他是Docker的前生,或者说Docker是LXC的使用者。完整的LXC能力在2008年合入Linux主线,所以容器的概念在2008年就基本定型了,并不是后面Docker造出来的。关于LXC的介绍很多,大体都会说“LXC是Linux内核提供的容器技术,能提供轻量级的虚拟化能力,能隔离进程和资源”,但总结起来,无外乎就两大知识点Cgroups(Linux Control Group)和Linux Namespace。搞清楚他俩,容器技术就基本掌握了。 - Cgroups:重点在“限制”。限制资源的使用,包括CPU、内存、磁盘的使用,体现出对资源的管理能力。 - Namespace:重点在“隔离”。隔离进程看到的Linux视图。说大白话就是,容器和容器之间不要相互影响,容器和宿主机之间不要相互影响。 ### 2、少年期起步艰难 2009年,Cloud Foundry基于LXC实现了对容器的操作,该项目取名为Warden。2010年,dotCloud公司同样基于LXC技术,使用Go语言实现了一款容器引擎,也就是现在的Docker。那时,dotCloud公司还是个小公司,出生卑微的Docker没什么热度,活得相当艰难。 ### 3、 成长为巨无霸 2013年,dotCloud公司决定将Docker开源。开源后,项目突然就火了。从大的说,火的原因就是Docker的这句口号“Build once,Run AnyWhere”。呵呵,是不是似曾相识?对的,和Java的Write Once,Run AnyWhere一个道理。对于一个程序员来说,程序写完后打包成镜像就可以随处部署和运行,开发、测试和生产环境完全一致,这是多么大一个诱惑。程序员再也不用去定位因环境差异导致的各种坑爹问题。 Docker开源项目的异常火爆,直接驱动dotCloud公司在2013年更名为Docker公司。Docker也快速成长,干掉了CoreOS公司的rkt容器和Google的lmctfy容器,直接变成了容器的事实标准。也就有了后来人一提到容器就认为是Docker。 总结起来,Docker为什么火,靠的就是Docker镜像。他打包了应用程序的所有依赖,彻底解决了环境的一致性问题,重新定义了软件的交付方式,提高了生产效率。 ### 4、 被列强蚕食 Docker在容器领域快速成长,野心自然也变大了。2014年推出了容器云产品Swarm(K8s的同类产品),想扩张事业版图。同时Docker在开源社区拥有绝对话语权,相当强势。这种走自己的路,让别人无路可走的行为,让容器领域的其他大厂玩家很是不爽,为了不让Docker一家独大,决定要干他。 2015年6月,在Google、Redhat等大厂的“运作”下,Linux基金会成立了OCI(Open Container Initiative)组织,旨在围绕容器格式和运行时制定一个开放的工业化标准,也就是我们常说的OCI标准。同时,Docker公司将Libcontainer模块捐给CNCF社区,作为OCI标准的实现,这就是现在的RunC项目。说白了,就是现在这块儿有个标准了,大家一起玩儿,不被某个特定项目的绑定。 讲到Docker,就得说说Google家的Kubernetes,他作为容器云平台的事实标准,如今已被广泛使用,俨然已成为大厂标配。Kubernetes原生支持Docker,让Docker的市场占有率一直居高不下。如图是2019年容器运行时的市场占有率。  但在2020年,Kubernetes突然宣布在1.20版本以后,也就是2021年以后,不再支持Docker作为默认的容器运行时,将在代码主干中去除dockershim。  如图所示,K8s自身定义了标准的容器运行时接口CRI(Container Runtime Interface),目的是能对接任何实现了CRI接口的容器运行时。在初期,Docker是容器运行时不容置疑的王者,K8s便内置了对Docker的支持,通过dockershim来实现标准CRI接口到Docker接口的适配,以此获得更多的用户。随着开源的容器运行时Containerd(实现了CRI接口,同样由Docker捐给CNCF)的成熟,K8s不再维护dockershim,仅负责维护标准的CRI,解除与某特定容器运行时的绑定。当然,也不是K8s不支持Docker了,只是dockershim谁维护的问题。 随着K8s态度的变化,预计将会有越来越多的开发者选择直接与开源的Containerd对接,Docker公司和Docker开源项目(现已改名为moby)未来将会发生什么样的变化,谁也说不好。  讲到这里,不知道大家有没有注意到,Docker公司其实是捐献了Containerd和runC。这俩到底是啥东西。简单的说,runC是OCI标准的实现,也叫OCI运行时,是真正负责操作容器的。Containerd对外提供接口,管理、控制着runC。所以上面的图,真正应该长这样。  Docker公司是一个典型的小公司因一个爆款项目火起来的案例,不管是技术层面、公司经营层面以及如何跟大厂缠斗,不管是好的方面还是坏的方面,都值得我们去学习和了解其背后的故事。 # 什么是容器 按国际惯例,在介绍一个新概念的时候,都得从大家熟悉的东西说起。幸好容器这个概念还算好理解,喝水的杯子,洗脚的桶,养鱼的缸都是容器。容器技术里面的“容器”也是类似概念,只是装的东西不同罢了,他装的是应用软件本身以及软件运行起来需要的依赖。用鱼缸来类比,鱼缸这个容器里面装的应用软件就是鱼,装的依赖就是鱼食和水。这样大家就能理解docker的logo了。大海就是宿主机,docker就是那条鲸鱼,鲸鱼背上的集装箱就是容器,我们的应用程序就装在集装箱里面。  在讲容器的时候一定绕不开容器镜像,这里先简单的把容器镜像理解为是一个压缩包。压缩包里包含应用的可执行程序以及程序依赖的文件(例如:配置文件和需要调用的动态库等),接下来通过实际操作来看看容器到底是个啥。 ## 一、宿主机视角看容器: **1、首先,我们启动容器。** `docker run -d --name="aimar-1-container" euleros_arm:2.0SP8SPC306 /bin/sh -c "while true; do echo aimar-1-container; sleep 1; done"` 这是Docker的标准命令。意思是使用euleros_arm:2.0SP8SPC306镜像(镜像名:版本号)创建一个新的名字为"aimar-1-container"的容器,并在容器中执行shell命令:每秒打印一次“aimar-1-container”。 - **参数说明:** -d:使用后台运行模式启动容器,并返回容器ID。 --name:为容器指定一个名字。 docker run -d --name="aimar-1-container" euleros_arm:2.0SP8SPC306 /bin/sh -c "while true; do echo aimar-1-container; sleep 1; done" 207b7c0cbd811791f7006cd56e17033eb430ec656f05b6cd172c77cf45ad093c 从输出中,我们看到一串长字符207b7c0cbd811791f7006cd56e17033eb430ec656f05b6cd172c77cf45ad093c。他就是容器ID,能唯一标识一个容器。当然在使用的时候,不需要使用全id,直接使用缩写id即可(全id的前几位)。例如下图中,通过docker ps查询到的容器id为207b7c0cbd81  aimar-1-container容器启动成功后,我们在宿主机上使用ps进行查看。这时可以发现刚才启动的容器就是个进程,PID为12280。  我们尝试着再启动2个容器,并再次在宿主机进行查看,你会发现又新增了2个进程,PID分别为20049和21097。  所以,我们可以得到一个结论。**从宿主机的视角看,容器就是进程。** **2、接下来,我们进入这个容器。** `docker exec -it 207b7c0cbd81 /bin/bash` docker exec也是Docker的标准命令,用于进入某个容器。意思是进入容器id为207b7c0cbd81的容器,进入后执行/bin/bash命令,开启命令交互。 - **参数说明:** -it其实是-i和-t两个参数,意思是容器启动后,要分配一个输入/输出终端,方便我们跟容器进行交互,实现跟容器的“对话”能力。  从hostname从kwephispra09909变化为207b7c0cbd81,说明我们已经进入到容器里面了。在容器中,我们尝试着启动一个新的进程。 `[root@207b7c0cbd81 /]# /bin/sh -c "while true; do echo aimar-1-container-embed; sleep 1; done" &`  再次回到宿主机进行ps查看,你会发现**不管是直接启动容器,还是在容器中启动新的进程,从宿主机的角度看,他们都是进程。** # 二、容器视角看容器: 前面我们已经进入容器里面,并启动了新的进程。但是我们并没有在容器里查看进程的情况。在容器中执行ps,会发现得到的结果和宿主机上执行ps的结果完全不一样。下图是容器中的执行结果。  在Container1容器中只能看见刚起启动的shell进程(container1和container1-embed),看不到宿主机上的其他进程,也看不到Container2和Container3里面的进程。这些进程像被关进了一个盒子里面,完全感知不到外界,甚至认为我们执行的container1是1号进程(1号进程也叫init进程,是系统中所有其他用户进程的祖先进程)。所以,**从容器的视角,容器觉得“我就是天,我就是地,欢迎来到我的世界”。**  但尴尬的是,在宿主机上,他们却是普通得不能再普通的进程。注意,相同的进程,在容器里看到的进程ID和在宿主机上看到的进程ID是不一样的。容器中的进程ID分别是1和1859,宿主机上对应的进程ID分别是12280和9775(见上图)。 # 三、总结 通过上面的实验,对容器的定义就需要再加上一个定语。**容器就是进程=>容器是与系统其他部分隔离开的进程。** 这个时候我们再看下图就更容易理解,容器是跑在宿主机OS(虚机容器的宿主机OS就是Guest OS)上的进程,容器间以及容器和宿主机间存在隔离性,例如:进程号的隔离。  在容器内和宿主机上,同一个进程的进程ID不同。例如:Container1在容器内PID是1,在宿主机上是12280。那么该进程真正的PID是什么呢?当然是12280!那为什么会造成在容器内看到的PID是1呢,造成这种幻象的,正是Linux Namespace。 Linux Namespace是Linux内核用来隔离资源的方式。每个Namespace下的资源对于其他Namespace都是不透明,不可见的。  Namespace按隔离的资源进行分类:  前面提到的容器内外,看到的进程ID不同,正是使用了PID Namespace。那么这个Namespace在哪呢?在Linux上一切皆文件。是的,这个Namespace就在文件里。在宿主机上的proc文件中(/proc/进程号/ns)变记录了某个进程对应的Namespace信息。如下图,其中的数字(例如:pid:[ 4026534312])则表示一个Namespace。  对于Container1、Container2、Container3这3个容器,我们可以看到,他们的PID Namespace是不一样的。说明他们3个容器中的PID相互隔离,也就是说,这3个容器里面可以同时拥有PID号相同的进程,例如:都有PID=1的进程。  在一个命名空间中,那这俩进程就相互可见,只是PID与宿主机上看到的不同而已。  至此,我们可以对容器的定义再细化一层。**容器是与系统其他部分隔离开的进程=》容器是使用Linux Namespace实现与系统其他部分隔离开的进程。**
-
本文目的:在沃土云环境的前提下搭建docker技术支持的微服务1.代码结构采用微服务编写,各个服务采用http,rpc都可,本文重点不在代码层面2.把单个服务用docker 运行起来,编写Dcokerfile 可参考:https://www.bookstack.cn/read/gin-EDDYCJY-blog/golang-gin-2018-03-24-Gin%E5%AE%9E%E8%B7%B5-%E8%BF%9E%E8%BD%BD%E4%B9%9D-%E5%B0%86Golang%E5%BA%94%E7%94%A8%E9%83%A8%E7%BD%B2%E5%88%B0Docker.md3.在沃土上编译golang的项目(需要注意) 编译命令需要使用环境变量CGO_ENABLED直接使用go build . 生成的可执行文件 会报 line 1: syntax error: unexpected word (expecting ")")这种情况要用CGO_ENABLED当CGO_ENABLED=1, 进行编译时, 会将文件中引用libc的库(比如常用的net包),以动态链接的方式生成目标文件。当CGO_ENABLED=0, 进行编译时, 则会把在目标文件中未定义的符号(外部函数)一起链接到可执行文件中。示例: CGO_ENABLED=0 go build -o projectName4.用docker网络或docker swarm搭建docker 网络,把各个微服务加到同一个网络中docker run --net=project-network --network-alias subService5.在项目根目录 docker build .6.注意,启动时先用构建出来的可执行文件,本地测试,可以运行,方可以运行在各个容器环境
-
>摘要: “一分钟,我要这个人的全部信息”,霸道总裁拍了拍你。本文分享自华为云社区[《【测试工具技术解密】大规模数据如何实现数据的高效追溯》](https://bbs.huaweicloud.com/blogs/307038?utm_source=zhihu&utm_medium=bbs-ex&utm_campaign=paas&utm_content=content),作者: 敏捷的小智。 网上流传着很多关于程序员和产品经理的段子,比如有程序员为了应对产品经理的需求变更,连续通宵加班改代码,都累晕过去了,怎么都叫不醒,最后产品经理喊了一句:“小程,我这里有两个需求还要再改改,你看看怎么改?”程序员嘭的一下就弹起来了,说:“你还有什么要求,我还可以,我还能改,下班前就能改完。” 这些虽然是段子,但是在现实生活中,需求也是随着用户的使用不断变更的,程序员经常要面对各种各样的需求变更,那么,怎么让我们更快的适应这些需求变更,更快的迭代交互版本呢?非常火热的微服务和微服务架构,又能给我们的软件开发带来什么好处和方便呢?不着急,后面慢慢道来。 举个例子,最开始,张三和李四看到最近微商很火,他们也想开发一个微信小程序做微商,线上卖货,把家乡的特产卖出去,最开始需求就很简单,有一个微信小程序,用户可以在小程序上浏览和选购商品就可以,在后台,需要管理商品、用户和订单数据。张三作为程序员梳理了一下,主要有如下功能点: 小程序前端: - 用户注册、登录 - 商品展示 - 下单购买 后台: - 用户管理 - 商品管理 - 订单管理 整个功能简单明了,张三作为资深程序员,很快就完成了代码的开发,在部署时,出于安全考虑,小明还将前端和后台做了分离部署,很快,小明从华为云上购买了ECS和RDS服务,将开发的应用部署上去,对接好微信小程序平台,小程序就上线了,整体架构如下图:  图1 最开始的整体架构图 小程序上线以后,由于家乡的特产原生态、质量好、风味佳,大受欢迎,顾客们好评如潮,张三和李四开始躺着数钱,做梦都笑不醒。可是好景不长,看到他们做的这么红火,其它人也开始抄袭并快速上线,极大的影响了张三他们的收入,在残酷的竞争压力下,张三和李四决定做一些营销活动,来发展和留住客户,增加收入。营销活动主要有如下的内容: - 拓展商品品类:以商城的形式销售其它厂家的商品 - 拓展渠道:新增网页版入口和单独的APP入口,即上线网页版、上线独立APP等; - 促销活动:比如满减、打折、充值优惠、会员优惠、会员积分兑换等 - 精准营销:购买一些三方数据,根据用户画像进行推送等等。 这些扩展的需求都需要开发程序来支持,张三这个时候就忙不过来了,因此他们新招了一个程序员王五,来一起开发,这次进行了分工,王五主要负责数据分析、会员业务及APP和网页版的开发,张三负责促销活动相关功能的开发。由于要快速上线,张三和王五头脑风暴了一把以后,大致对系统做了一下划分,主要把促销管理、数据分析、会员管理、对接第三方厂家数据等放到后台里,网页和移动端APP另外搭建,忙活了一个多星期,张三和王五完成了基本功能的开发,当前的整体架构如下:  图2 增加促销和单独入口后的架构图 这一阶段存在很多不合理的地方: - 小程序、APP和网站3个前端分别开发,有较多的重复开发工作。 - 所有数据都存在一个数据库中,促销时订单数据等的访问影响会员管理等数据访问的性能,出现过促销时用户无法充会员的情况,流失收入 - 数据共享有时是通过数据库共享的,有些又通过应用间的接口来提供,应用间相互耦合,依赖关系混乱。 - 单个应用上不停的扩充功能,应用功能越来越多,代码也越来越多,维护和开发都越来越复杂,且应用上线周期变长,上线后如有问题影响变大。 - 后台之间相互影响,用户管理影响会员管理,订单管理也会影响用户和会员管理,后台逻辑混乱,维护坤看。 - 数据库表结构被多个应用依赖,无法重构和优化。 - 数据分析使用数据库存储数据来实现,且与实时业务库在同一个库,数据分析消耗大量的数据库性能,影响业务访问数据库的性能,页面出现卡顿等。 - 生产环境的维护变得困难,生产环境的升级变更变得困难,由于不解耦,修改功能后需要前端和后台同时上线,变更时会影响用户使用,需要停服变更等,这样变更时间就只能放到凌晨,开发和运维都很累。 - 在后台中的公共功能开发,出现程序员之间相互推诿的情况,每个人都觉得公共功能应该有对方开发,自己只做自己负责的业务开发,要不就是都单独开发,代码冗余越来越多。 虽然存在着很多问题,但也不能否认这一阶段的成果:程序员们快速的完成了系统的开发,并适应业务的变化,完成了新的业务开发,不过因为都是快速开发上线,开发过程中都容易只着眼于快速实现业务功能,容易陷入局部、短浅的思维方式,针对功能妥协,缺乏全局的、长远的设计,也缺乏对扩展性、性能等的设计和考虑,长此以往,系统建设将会越来越困难,甚至陷入不断推翻、重建的循环。 张三和王五都是有追求的程序员,他们对现状不满,当线上运行稍微稳定以后,他们去学习了业界流行的微服务架构,并系统的开始对商城进行梳理,开始梳理系统的整体架构并进行微服务改造,他们开始对系统进行抽象,他们重新梳理了商城的业务逻辑,抽象出公共的能力,做成公共的微服务,主要如下: - 用户服务 - 会员服务 - 商品服务 - 订单服务 - 促销服务 - 数据分析服务 抽象完成后,各个应用服务只需要从公共服务获取所需的数据,删除了大量的冗余代码。然后针对之前数据库性能瓶颈问题和数据管理混乱和数据库表结构复杂的问题,张三他们分析,如果继续保持共用数据库,则整个架构会持续的僵化,失去了微服务设计的意义,因此他们一鼓作气,将数据库也进行了拆分,并引入了消息队列实现系统的解耦和通信的实时性,引入数据仓库来进行数据分析,梳理完后的整体架构如下:  图3 微服务改造和数据库分离后的架构图 如上图,完全拆分后各个服务可以采用异构的技术,每个服务可以使用独立的技术栈单独开发,每个服务都可以单独的部署上线。比如数据分析服务可以使用华为云数据仓库作为持久化层,以便于高效地做一些统计计算;商品服务和会员服务访问频率比较大,因此使用华为云DCS来作为缓存,加入了缓存机制等。微服务之间使用了华为云的DMS来进行消息通信,提升了消息通信的速度同时服务间实现了更好的解耦。微服务架构要求服务都单独的使用数据库,优点是明显的,服务间完全隔离,但数据库拆分也有一些问题和挑战:比如说跨库级联的需求,通过服务查询数据颗粒度的粗细问题等。但是这些问题可以通过合理的设计来解决。总体来说,数据库拆分是一个利大于弊的。 微服务架构还有一个技术外的好处,它使整个系统的分工更加明确,责任更加清晰,每个人专心负责为其他人提供更好的服务。在单体应用的时代,公共的业务功能经常没有明确的归属。最后要么各做各的,每个人都重新实现了一遍;要么是随机一个人(一般是能力比较强或者比较热心的人)做到他负责的应用里面。在后者的情况下,这个人在负责自己应用之外,还要额外负责给别人提供这些公共的功能——而这个功能本来是无人负责的,仅仅因为他能力较强/比较热心,就莫名地背锅(这种情况还被美其名曰能者多劳)。结果最后大家都不愿意提供公共的功能。长此以往,团队里的人渐渐变得各自为政,不再关心全局的架构设计。 从这个角度上看,使用微服务架构同时也需要组织结构做相应的调整。所以说做微服务改造需要管理者的支持。改造完成后,张三和王五分清楚各自的锅。两人十分满意,对系统和当前的开发模式都很满意。 但是,微服务架构调整完以后,就没有其他问题了吗?未完待续…… 附:华为云现在提供微服务专家服务,为云为客户提供专属[微服务咨询服务](https://www.huaweicloud.com/service/microservice_pro.html?from=bk),以“适用性评估,目标制定,差距分析,试点实施,效果评估,经验固化”全面指导为设计思路,协助客户高效、低成本的完成微服务规范、工具、平台和流程等整套体系建设,提升业务创新效率。 【参考资料】 - 华为云微服务专家服务 - 微服务架构-https://insights.thoughtworks.cn/microservices-martin-fowler/ - Wiki百科微服务:https://zh.wikipedia.org/wiki/%E5%BE%AE%E6%9C%8D%E5%8B%99
-
小公司 15 人的研发团队,适合干微服务吗,现在把系统拆解以后,排查问题的复杂度上升,而且目前我们的部署方式是取仓库的某一个分支给所有容器升级,感觉和之前单机区别不是特别大,各个组之间甩锅,真的都有些怀疑要不要继续搞下去
-
随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。这些服务可能使用不同编程语言开发,由不同团队开发,也可能部署很多副本。因此,就需要一些可以帮助理解系统行为、用于分析性能问题的工具,以便发生故障的时候,能够快速定位和解决问题。 全链路监控组件就在这样的问题背景下产生了。 全链路性能监控,从整体维度到局部维度展示各项指标,将跨应用的所有调用链性能信息集中展现,可方便度量整体和局部性能,并且方便找到故障产生的源头,生产上可极大缩短故障排除时间。全链路监控系统主要功能:• 请求链路追踪:通过分析服务调用关系,绘制运行时拓扑信息,可视化展示• 调用情况衡量:各个调用环节的性能分析,例如吞吐量、响应时间、错误次数• 容器规划参考:扩容/缩容、服务降级、流量控制• 运行情况反馈:告警,通过调用链结合业务日志快速定位错误信息如何选择全链路监控系统,主要考虑以下几点:• 探针的性能消耗APM组件服务的影响应该做到足够小,数据分析要快,性能占用小。• 代码的侵入性即也作为业务组件,应当尽可能少入侵或者无入侵其他业务系统,对于使用方透明,减少开发人员的负担。• 监控维度分析的维度尽可能多。• 可扩展性一个优秀的调用跟踪系统必须支持分布式部署,具备良好的可扩展性。能够支持的组件越多当然越好。
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签