当前位置: 首页 > news >正文

做的网站浏览器提示不安全网站开发后端待遇

做的网站浏览器提示不安全,网站开发后端待遇,深圳龙华住房和建设局网站官网,网络营销产品概念Spring Cloud微服务项目交付 微服务扫盲篇 微服务并没有一个官方的定义#xff0c;想要直接描述微服务比较困难#xff0c;我们可以通过对比传统WEB应用#xff0c;来理解什么是微服务。 单体应用架构 如下是传统打车软件架构图#xff1a; 这种单体应用比较适合于小项…Spring Cloud微服务项目交付 微服务扫盲篇 微服务并没有一个官方的定义想要直接描述微服务比较困难我们可以通过对比传统WEB应用来理解什么是微服务。 单体应用架构 如下是传统打车软件架构图 这种单体应用比较适合于小项目优点是 开发简单直接集中式管理基本不会重复开发功能都在本地没有分布式的管理开销和调用开销 当然它的缺点也十分明显特别对于互联网公司来说 开发效率低所有的开发在一个项目改代码递交代码相互等待代码冲突不断代码维护难代码功能耦合在一起新人不知道何从下手部署不灵活构建时间长任何小修改必须重新构建整个项目这个过程往往很长稳定性不高一个微不足道的小问题可以导致整个应用挂掉扩展性不够无法满足高并发情况下的业务需求 微服务应用架构 微服务架构的设计思路不是开发一个巨大的单体式应用而是将应用分解为小的、互相连接的微服务。一个微服务完成某个特定功能比如乘客管理和下单管理等。每个微服务都有自己的业务逻辑和适配器。一些微服务还会提供API接口给其他微服务和应用客户端使用。 比如前面描述的系统可被分解为 每个业务逻辑都被分解为一个微服务微服务之间通过REST API通信。一些微服务也会向终端用户或客户端开发API接口。但通常情况下这些客户端并不能直接访问后台微服务而是通过API Gateway来传递请求。API Gateway一般负责服务路由、负载均衡、缓存、访问控制和鉴权等任务。 微服务架构优点 解决了复杂性问题。它将单体应用分解为一组服务。虽然功能总量不变但应用程序已被分解为可管理的模块或服务体系结构使得每个服务都可以由专注于此服务的团队独立开发。只要符合服务API契约开发人员可以自由选择开发技术。这就意味着开发人员可以采用新技术编写或重构服务由于服务相对较小所以这并不会对整体应用造成太大影响微服务架构可以使每个微服务独立部署。这些更改可以在测试通过后立即部署。所以微服务架构也使得CICD成为可能 微服务架构问题及挑战 微服务的一个主要缺点是微服务的分布式特点带来的复杂性。开发人员需要基于RPC或者消息实现微服务之间的调用和通信而这就使得服务之间的发现、服务调用链的跟踪和质量问题变得的相当棘手。 微服务的一大挑战是跨多个服务的更改 比如在传统单体应用中若有A、B、C三个服务需要更改A依赖BB依赖C。我们只需更改相应的模块然后一次性部署即可。 在微服务架构中我们需要仔细规划和协调每个服务的变更部署。我们需要先更新C然后更新B最后更新A。 部署基于微服务的应用也要复杂得多 单体应用可以简单的部署在一组相同的服务器上然后前端使用负载均衡即可。微服务由不同的大量服务构成。每种服务可能拥有自己的配置、应用实例数量以及基础服务地址。这里就需要不同的配置、部署、扩展和监控组件。此外我们还需要服务发现机制以便服务可以发现与其通信的其他服务的地址 以上问题和挑战可大体概括为 API Gateway服务间调用服务发现服务容错服务部署数据调用 https://www.kancloud.cn/owenwangwen/open-capacity-platform/1480155自助餐吃吃喝喝竟然秒懂微服务 微服务框架 如何应对上述挑战出现了如下微服务领域的框架 Spring Cloud各个微服务基于Spring Boot实现DubboService Mesh LinkerdEnvoyConduitIstio 了解Spring Cloud https://spring.io 核心项目及组件 https://spring.io/projects 与Dubbo对比 做一个简单的功能对比 核心要素DubboSpring Cloud服务注册中心ZookeeperSpring Cloud Netflix Eureka服务调用方式RPCREST API服务监控Dubbo-monitorSpring Boot Admin断路器不完善Spring Cloud Netflix Hystrix服务网关无Spring Cloud Netflix Zuul分布式配置无Spring Cloud Config服务跟踪无Spring Cloud Sleuth消息总线无Spring Cloud Bus数据流无Spring Cloud Stream批量任务无Spring Cloud Task……………… 从上图可以看出其实Dubbo的功能只是Spring Cloud体系的一部分。 这样对比是不够公平的首先Dubbo是SOA时代的产物它的关注点主要在于服务的调用流量分发、流量监控和熔断。而Spring Cloud诞生于微服务架构时代考虑的是微服务治理的方方面面另外由于依托了Spirng、Spirng Boot的优势之上两个框架在开始目标就不一致Dubbo定位服务治理、Spirng Cloud是一个生态。 Spring Boot交付实践 从零开始创建Spring Boot项目 通过File New Project新建工程选择Spring Initializr 配置Project Metadata 配置Dependencies依赖包 选择Web分类中的Spring web和Template Engines中的Thymeleaf 配置maven settings.xml 默认使用IDE自带的maven换成自己下载的下载地址 链接: https://pan.baidu.com/s/1z9dRGv_4bS1uxBtk5jsZ2Q 提取码: 3gva 解压后放到D:\software\apache-maven-3.6.3,修改D:\software\apache-maven-3.6.3\conf\settings.xml 文件 ?xml version1.0 encodingUTF-8? settings xmlnshttp://maven.apache.org/SETTINGS/1.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsdlocalRepositoryD:\opt\maven-repo/localRepositorypluginGroups/pluginGroupsproxies/proxiesservers/serversmirrorsmirroridalimaven/idmirrorOfcentral/mirrorOfnamealiyun maven/nameurlhttp://maven.aliyun.com/nexus/content/repositories/central//url/mirrormirroridnexus-aliyun/idmirrorOf*/mirrorOfnameNexus aliyun/nameurlhttp://maven.aliyun.com/nexus/content/groups/public/url/mirror/mirrors/settings替换springboot版本为2.3.5.RELEASE 直接启动项目并访问本地服务localhost:8080 编写功能代码 创建controller包及HelloController.java文件 package com.luffy.demo.controller;import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController;RestController public class HelloController {RequestMapping(value /hello, method RequestMethod.GET)public String hello(String name) {return Hello, name;} 保存并在浏览器中访问localhost:8080/hello?nameluffy 如果页面复杂如何实现 在resources/templates/目录下新建index.html !DOCTYPE html html headtitleDevops/titlemeta http-equivContent-Type contenttext/html; charsetUTF-8 / /head body div classcontainerh3 th:text${requestname}/h3a idrightaway href# th:href{/rightaway} 立即返回/aa idsleep href# th:href{/sleep}延时返回/a /div /body /html完善HelloController.java的内容 package com.luffy.demo.controller;import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView;RestController public class HelloController {RequestMapping(value /hello, method RequestMethod.GET)public String hello(String name) {return Hello, name;}RequestMapping(/)public ModelAndView index(ModelAndView mv) {mv.setViewName(index);mv.addObject(requestname, This is index);return mv;}RequestMapping(/rightaway)public ModelAndView returnRightAway(ModelAndView mv) {mv.setViewName(index);mv.addObject(requestname,This request is RightawayApi);return mv;}RequestMapping(/sleep)public ModelAndView returnSleep(ModelAndView mv) throws InterruptedException {Thread.sleep(2*1000);mv.setViewName(index);mv.addObject(requestname,This request is SleepApi,it will sleep 2s !);return mv;} } 如何在java项目中使用maven 为什么需要maven 考虑一个常见的场景以项目A为例开发过程中需要依赖B-2.0.jar的包如果没有maven那么正常做法是把B-2.0.jar拷贝到项目A中但是如果B-2.0.jar还依赖C.jar我们还需要去找到C.jar的包因此在开发阶段需要花费在项目依赖方面的精力会很大。 因此开发人员需要找到一种方式可以管理java包的依赖关系并可以方便的引入到项目中。 maven如何工作 查看pom.xml dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-thymeleaf/artifactId/dependency可以直接在项目中添加上dependency 这样来指定项目的依赖包。 思考如果spring-boot-starter-thymeleaf包依赖别的包怎么办 spring-boot-starter-thymeleaf同时也是一个maven项目也有自己的pom.xml 查看一下 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactIdversion2.3.3.RELEASE/versionscopecompile/scope/dependencydependencygroupIdorg.thymeleaf/groupIdartifactIdthymeleaf-spring5/artifactIdversion3.0.11.RELEASE/versionscopecompile/scope/dependencydependencygroupIdorg.thymeleaf.extras/groupIdartifactIdthymeleaf-extras-java8time/artifactIdversion3.0.4.RELEASE/versionscopecompile/scope/dependency/dependencies这样的话使用maven的项目只需要在自己的pom.xml中把所需的最直接的依赖包定义上而不用关心这些被依赖的jar包自身是否还有别的依赖。剩下的都交给maven去搞定。 如何搞定maven可以根据pom.xml中定义的依赖实现包的查找 去哪查找maven仓库存储jar包的地方。 当我们执行 Maven 构建命令时Maven 开始按照以下顺序查找依赖的库 本地仓库 Maven 的本地仓库在安装 Maven 后并不会创建它是在第一次执行 maven 命令的时候才被创建。 运行 Maven 的时候Maven 所需要的任何包都是直接从本地仓库获取的。如果本地仓库没有它会首先尝试从远程仓库下载构件至本地仓库然后再使用本地仓库的包。 默认情况下不管Linux还是 Windows每个用户在自己的用户目录下都有一个路径名为 .m2/respository/ 的仓库目录。 Maven 本地仓库默认被创建在 %USER_HOME% 目录下。要修改默认位置在 %M2_HOME%\conf 目录中的 Maven 的 settings.xml 文件中定义另一个路径。 settings xmlnshttp://maven.apache.org/SETTINGS/1.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsdlocalRepositoryD:\opt\maven-repo/localRepository /settings中央仓库 Maven 中央仓库是由 Maven 社区提供的仓库中央仓库包含了绝大多数流行的开源Java构件以及源码、作者信息、SCM、信息、许可证信息等。一般来说简单的Java项目依赖的构件都可以在这里下载到。 中央仓库的关键概念 这个仓库由 Maven 社区管理。不需要配置maven中集成了地址 http://repo1.maven.org/maven2需要通过网络才能访问。 私服仓库 通常使用 sonatype Nexus来搭建私服仓库。搭建完成后需要在 setting.xml中进行配置比如 profileidlocalRepository/idrepositoriesrepositoryidmyRepository/idnamemyRepository/nameurlhttp://127.0.0.1:8081/nexus/content/repositories/myRepository//urlreleasesenabledtrue/enabled/releasessnapshotsenabledtrue/enabled/snapshots/repository/repositories /profile方便起见我们直接使用国内ali提供的仓库修改 maven 根目录下的 conf 文件夹中的 setting.xml 文件在 mirrors 节点上添加内容如下 mirrorsmirroridalimaven/idnamealiyun maven/nameurlhttp://maven.aliyun.com/nexus/content/groups/public//urlmirrorOfcentral/mirrorOf /mirror /mirrors在执行构建的时候maven会自动将所需的包下载到本地仓库中所以第一次构建速度通常会慢一些后面速度则很快。 那么maven是如何找到对应的jar包的 我们可以访问 https://mvnrepository.com/ 查看在仓库中的jar包的样子。 !-- https://mvnrepository.com/artifact/commons-collections/commons-collections -- dependencygroupIdcommons-collections/groupIdartifactIdcommons-collections/artifactIdversion3.2.2/version /dependency刚才看到spring-boot-starter-thymeleaf的依赖同样有上述属性因此maven就可以根据这三项属性到对应的仓库中去查找到所需要的依赖包并下载到本地。 其中groupId、artifactId、version共同保证了包在仓库中的唯一性这也就是为什么maven项目的pom.xml中都先配置这几项的原因因为项目最终发布到远程仓库中供别人调用。 思考我们项目的dependency中为什么没有写version ? 是因为sprintboot项目的上面有人来看一下项目parent的写法 parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.3.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentparent模块中定义过的dependencies在子项目中引用的话不需要指定版本这样可以保证所有的子项目都使用相同版本的依赖包。 生命周期及mvn命令实践 Maven有三套相互独立的生命周期分别是clean、default和site。每个生命周期包含一些阶段phase阶段是有顺序的后面的阶段依赖于前面的阶段。 clean生命周期清理项目 清理mvn clean    --删除target目录也就是将class文件等删除 default生命周期项目的构建等核心阶段 编译mvn compile  --src/main/java目录java源码编译生成class target目录下测试mvn test    --src/test/java 执行目录下的测试用例打包mvn package  --生成压缩文件java项目#jar包web项目#war包也是放在target目录下安装mvn install   --将压缩文件(jar或者war)上传到本地仓库部署|发布mvn deploy  --将压缩文件上传私服 site生命周期建立和发布项目站点 站点 : mvn site --生成项目站点文档 各个生命周期相互独立一个生命周期的阶段前后依赖。 生命周期阶段需要绑定到某个插件的目标才能完成真正的工作比如test阶段正是与maven-surefire-plugin的test目标相绑定了 。 举例如下 mvn clean 调用clean生命周期的clean阶段 mvn test 调用default生命周期的test阶段实际执行test以及之前所有阶段 mvn clean install 调用clean生命周期的clean阶段和default的install阶段实际执行cleaninstall以及之前所有阶段 在linux环境中演示 创建gitlab组luffy-spring-cloud,在该组下创建项目springboot-demo 提交代码到git仓库 $ git init $ git remote add origin http://gitlab.luffy.com/luffy-spring-cloud/springboot-demo.git $ git add . $ git commit -m Initial commit $ git push -u origin master使用tools容器来运行 $ docker run --rm -ti 172.21.51.67:5000/devops/tools:v3 bash bash-5.0# mvn -v bash: mvn: command not found # 由于idea工具自带了maven所以可以直接在ide中执行mvn命令。在tools容器中需要安装mvn命令为tools镜像集成mvn 将本地的apache-maven-3.6.3放到tools项目中修改settings.xml配置 ... localRepository/opt/maven-repo/localRepository ...然后修改Dockerfile添加如下部分: #-----------------安装 maven--------------------# COPY apache-maven-3.6.3 /usr/lib/apache-maven-3.6.3 RUN ln -s /usr/lib/apache-maven-3.6.3/bin/mvn /usr/local/bin/mvn chmod x /usr/local/bin/mvn ENV MAVEN_HOME/usr/lib/apache-maven-3.6.3 #------------------------------------------------#去master节点拉取最新代码构建最新的tools镜像 # k8s-master节点$ git pull$ docker build . -t 172.21.51.67:5000/devops/tools:v4 -f Dockerfile$ docker push 172.21.51.67:5000/devops/tools:v4再次尝试mvn命令 $ docker run --rm -ti 172.21.51.67:5000/devops/tools:v4 bash bash-5.0# mvn -v bash-5.0# git clone http://gitlab.luffy.com/luffy-spring-cloud/springboot-demo.git bash-5.0# cd springboot-demo bash-5.0# mvn clean # 观察/opt/maven目录 bash-5.0# mvn package # 多阶段组合 bash-5.0# mvn clean package想系统学习maven可以参考 https://www.runoob.com/maven/maven-pom.html Springboot服务镜像制作 通过mvn package命令拿到服务的jar包后我们可以使用如下命令启动服务 $ java -jar demo-0.0.1-SNAPSHOT.jar因此需要准备Dockerfile来构建镜像 FROM openjdk:8-jdk-alpine COPY target/springboot-demo-0.0.1-SNAPSHOT.jar app.jar CMD [ sh, -c, java -jar /app.jar ]我们可以为构建出的镜像指定名称 buildfinalName${project.artifactId}/finalName!--打jar包去掉版本号--...Dockerfile对应修改 FROM openjdk:8-jdk-alpine COPY target/springboot-demo.jar app.jar CMD [ sh, -c, java -jar /app.jar ]执行镜像构建验证服务启动是否正常 $ docker build . -t springboot-demo:v1 -f Dockerfile$ docker run -d --name springboot-demo -p 8080:8080 springboot-demo:v1$ curl localhost:8080接入CICD流程 之前已经实现了shared-library并且把python项目接入到了CICD 流程中。因此可以直接使用已有的流程把spring boot项目接入进去。 Jenkinsfilesonar-project.propertiesdeploy/deployment.yamldeploy/service.yamldeploy/ingress.yamlconfigmap/devops-config Jenkinsfile Library(luffy-devops) _pipeline {agent { label jnlp-slave}options {timeout(time: 20, unit: MINUTES)gitLabConnection(gitlab)}environment {IMAGE_REPO 172.21.51.67:5000/demo/springboot-demoIMAGE_CREDENTIAL credential-registryDINGTALK_CREDS credentials(dingTalk)PROJECT springboot-demo}stages {stage(checkout) {steps {container(tools) {checkout scm}}}stage(mvn-package) {steps {container(tools) {script{sh mvn clean package}}}}stage(CI){failFast trueparallel {stage(Unit Test) {steps {echo Unit Test Stage Skip...}}stage(Code Scan) {steps {container(tools) {script {devops.scan().start()}}}}}}stage(docker-image) {steps {container(tools) {script{devops.docker(${IMAGE_REPO},${GIT_COMMIT},IMAGE_CREDENTIAL).build().push()}}}}stage(deploy) {steps {container(tools) {script{devops.deploy(deploy,true,deploy/deployment.yaml).start()}}}}}post {success {script{devops.notificationSuccess(PROJECT,dingTalk)}}failure {script{devops.notificationFailure(PROJECT,dingTalk)}}} }sonar-project.properties sonar.projectKeyspringboot-demo sonar.projectNamespringboot-demo # if you want disabled the DTD verification for a proxy problem for example, true by default # JUnit like test report, default value is test.xml sonar.sourcessrc/main/java sonar.languagejava sonar.testssrc/test/java sonar.java.binariestarget/classesdeploy/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:name: springboot-demonamespace: {{NAMESPACE}} spec:replicas: 1selector:matchLabels:app: springboot-demotemplate:metadata:labels:app: springboot-demospec:containers:- name: springboot-demoimage: {{IMAGE_URL}}imagePullPolicy: IfNotPresentports:- containerPort: 8080resources:requests:memory: 100Micpu: 50mlimits:memory: 500Micpu: 100mlivenessProbe:httpGet:path: /port: 8080scheme: HTTPinitialDelaySeconds: 120periodSeconds: 15timeoutSeconds: 3readinessProbe:httpGet:path: /port: 8080scheme: HTTPinitialDelaySeconds: 120timeoutSeconds: 2periodSeconds: 15deploy/service.yaml apiVersion: v1 kind: Service metadata:name: springboot-demonamespace: {{NAMESPACE}} spec:ports:- port: 8080protocol: TCPtargetPort: 8080selector:app: springboot-demosessionAffinity: Nonetype: ClusterIP status:loadBalancer: {}deploy/ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata:name: springboot-demonamespace: {{NAMESPACE}} spec:rules:- host: {{INGRESS_SPRINGBOOTDEMO}}http:paths:- backend:serviceName: springboot-demoservicePort: 8080path: / status:loadBalancer: {}维护devops-config的configmap添加INGRESS_SPRINGBOOTDEMO配置项 $ kubectl -n dev edit cm devops-config ... data:INGRESS_MYBLOG: blog-dev.luffy.comINGRESS_SPRINGBOOTDEMO: springboot-dev.luffy.comNAMESPACE: dev ...更新Jenkins中的jnlp-slave-pod模板镜像 172.21.51.67:5000/devops/tools:v4由于镜像中maven的目录是/opt/maven-repo而slave-pod是执行完任务后会销毁因此需要将maven的数据目录挂载出来不然每次构建都会重新拉取所有依赖的jar包 配置Jenkins流水线 添加单元测试覆盖率 单元测试这块内容一直没有把覆盖率统计到sonarqube端本节看下怎么样将单元测试的结果及覆盖率展示到Jenkins及sonarqube平台中。 为了展示效果我们先添加一个单元测试文件HelloControllerTest package com.luffy.demo;import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext;SpringBootTest WebAppConfiguration public class HelloControllerTests {private static final Logger logger LoggerFactory.getLogger(HelloControllerTests.class);Autowiredprivate WebApplicationContext webApplicationContext;private MockMvc mockMvc;BeforeEachpublic void setMockMvc() {mockMvc MockMvcBuilders.webAppContextSetup(webApplicationContext).build();}Testpublic void index(){try {mockMvc.perform(MockMvcRequestBuilders.post(/).contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());}catch (Exception e) {e.printStackTrace();}}Testpublic void rightaway(){try {mockMvc.perform(MockMvcRequestBuilders.post(/rightaway).contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());}catch (Exception e) {e.printStackTrace();}}Testpublic void sleep(){try {mockMvc.perform(MockMvcRequestBuilders.post(/sleep).contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());}catch (Exception e) {e.printStackTrace();}} }jacoco监控JVM中的调用生成监控结果默认保存在jacoco.exec文件中然后分析此结果配合源代码生成覆盖率报告。 如何引入jacoco测试 plugingroupIdorg.jacoco/groupIdartifactIdjacoco-maven-plugin/artifactIdversion0.7.8/versionexecutionsexecutiongoalsgoalprepare-agent/goal/goalsconfigurationdestFile${project.build.directory}/coverage-reports/jacoco.exec/destFile/configuration/executionexecutioniddefault-report/idphasetest/phasegoalsgoalreport/goal/goalsconfigurationdataFile${project.build.directory}/coverage-reports/jacoco.exec/dataFileoutputDirectory${project.reporting.outputDirectory}/jacoco/outputDirectory/configuration/execution/executions/plugin其中 prepare-agent会把agent准备好这样在执行用例的时候就会使用agent检测到代码执行的过程通常将结果保存在jacoco.exec中report分析保存的jacoco.exec文件生成报告 在IDE中添加观察插件的goal执行mvn test观察执行过程。 有了上述内容后如何将结果发布到sonarqube中 提交最新代码查看sonarqube的分析结果。 Spring Cloud开发、交付实践 https://spring.io/projects/spring-cloud#overview 1、Netflix是一家做视频的网站可以这么说该网站上的美剧应该是最火的。 2、Netflix是一家没有CTO的公司正是这样的组织架构能使产品与技术无缝的沟通从而能快速迭代出更优秀的产品。在当时软件敏捷开发中Netflix的更新速度不亚于当年的微信后台变更虽然微信比Netflix迟发展但是当年微信的灰度发布和敏捷开发应该算是业界最猛的。 3、Netflix由于做视频的原因访问量非常的大从而促使其技术快速的发展在背后支撑着也正是如此Netflix开始把整体的系统往微服务上迁移。 4、Netflix的微服务做的不是最早的但是确是最大规模的在生产级别微服务的尝试。也正是这种大规模的生产级别尝试在服务器运维上依托AWS云。当然AWS云同样受益于Netflix的大规模业务不断的壮大。 5、Netflix的微服务大规模的应用在技术上毫无保留的把一整套微服务架构核心技术栈开源了出来叫做Netflix OSS也正是如此在技术上依靠开源社区的力量不断的壮大。 6、Spring Cloud是构建微服务的核心而Spring Cloud是基于Spring Boot来开发的。 7、Pivotal在Netflix开源的一整套核心技术产品线的同时做了一系列的封装就变成了Spring Cloud虽然Spring Cloud到现在为止不只有Netflix提供的方案可以集成还有很多方案但Netflix是最成熟的。 本课程基于SpringBoot 2.3.6.RELEASE 和Spring Cloud Hoxton.SR9版本 微服务场景 开发APP提供个人的花呗账单管理。 注册、登录、账单查询用户服务账单管理服务 Eureka服务注册中心 在SpringCloud体系中我们知道服务之间的调用是通过http协议进行调用的。而注册中心的主要目的就是维护这些服务的服务列表。 https://docs.spring.io/spring-cloud-netflix/docs/2.2.5.RELEASE/reference/html/ 新建项目 pom中引入spring-cloud的依赖 https://spring.io/projects/spring-cloud#overview propertiesspring.cloud-versionHoxton.SR9/spring.cloud-version /properties dependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring.cloud-version}/versiontypepom/typescopeimport/scope/dependency/dependencies /dependencyManagement引入eureka-server的依赖 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-server/artifactId/dependency启动eureka服务 https://docs.spring.io/spring-cloud-netflix/docs/2.2.5.RELEASE/reference/html/#spring-cloud-eureka-server-standalone-mode application.yml server:port: 8761eureka:client:service-url:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/register-with-eureka: falsefetch-registry: falseinstance:hostname: localhost启动类 package com.luffy.eureka;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;SpringBootApplication EnableEurekaServer public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);} }启动访问localhost:8761测试 创建spring cloud项目三部曲 引入依赖包修改application.yml配置文件启动类添加注解 eureka认证 没有认证不安全添加认证 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencyapplication.yml server:port: 8761 eureka:client:service-url:defaultZone: http://${spring.security.user.name}:${spring.security.user.password}${eureka.instance.hostname}:${server.port}/eureka/register-with-eureka: falsefetch-registry: falseinstance:hostname: localhost spring:security:user:name: ${EUREKA_USER:admin}password: ${EUREKA_PASS:admin}注册服务到eureka 新建项目user-service选择Spring Cloud依赖和SpringBoot Web依赖用来提供用户查询功能。 三部曲 pom.xml并添加依赖创建application.yml配置文件创建Springboot启动类并配置注解 pom.xml添加 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencyapplication.yml server:port: 7000 eureka:client:serviceUrl:defaultZone: http://${EUREKA_USER:admin}:${EUREKA_PASS:admin}localhost:8761/eureka/启动类 package com.luffy.user;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;//注意这里也可使用EnableEurekaClient //但由于springcloud是灵活的注册中心支持eureka、consul、zookeeper等 //若写了具体的注册中心注解则当替换成其他注册中心时又需要替换成对应的注解了。 //所以 直接使用EnableDiscoveryClient 启动发现。 //这样在替换注册中心时只需要替换相关依赖即可。 EnableDiscoveryClient SpringBootApplication public class UserServiceApplication {public static void main(String[] args) {SpringApplication.run(UserServiceApplication.class, args);} }报错 c.n.d.s.t.d.RetryableEurekaHttpClient : Request execution failed with message: com.fasterxml.jackson.databind.exc.MismatchedInputException: Root name timestamp does not match expected (instance) for type [simple type, class com.netflix.appinfo.InstanceInfo] 新版本的security默认开启csrf了关掉在注册中心新建一个类继承WebSecurityConfigurerAdapter来关闭 , 注意是在eureka server端关闭。 package com.luffy.eureka;import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;EnableWebSecurity Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter {Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable(); //关闭csrfhttp.authorizeRequests().anyRequest().authenticated().and().httpBasic(); //开启认证} }再次启动发现可以注册但是地址是 application.yaml server:port: 7000 eureka:client:serviceUrl:defaultZone: http://${EUREKA_USER:admin}:${EUREKA_PASS:admin}localhost:8761/eureka/instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: user-service spring:application:name: user-serviceEurake有一个配置参数eureka.server.renewalPercentThreshold定义了renews 和renews threshold的比值默认值为0.85。当server在15分钟内比值低于percent即少了15%的微服务心跳server会进入自我保护状态 默认情况下如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳Eureka Server将会注销该实例默认90秒。但是当网络分区故障发生时微服务与Eureka Server之间无法正常通信这就可能变得非常危险了因为微服务本身是健康的此时本不应该注销这个微服务。 Eureka Server通过“自我保护模式”来解决这个问题当Eureka Server节点在短时间内丢失过多客户端时可能发生了网络分区故障那么这个节点就会进入自我保护模式。一旦进入该模式Eureka Server就会保护服务注册表中的信息不再删除服务注册表中的数据也就是不会注销任何微服务。当网络故障恢复后该Eureka Server节点会自动退出自我保护模式。 自我保护模式是一种对网络异常的安全保护措施。使用自我保护模式而让Eureka集群更加的健壮、稳定。 开发阶段可以通过配置eureka.server.enable-self-preservationfalse关闭自我保护模式。 生产阶段理应以默认值进行配置。 至于具体具体的配置参数可至官网查看http://cloud.spring.io/spring-cloud-static/Finchley.RELEASE/single/spring-cloud.html#_appendix_compendium_of_configuration_properties 高可用 高可用 优先保证可用性各个节点都是平等的1个节点挂掉不会影响正常节点的工作剩余的节点依然可以提供注册和查询服务在向某个Eureka注册时如果发现连接失败则会自动切换至其它节点只要有一台Eureka还在就能保证注册服务可用(保证可用性) 注意点 多实例的话eureka.instance.instance-id需要保持不一样否则会当成同一个eureka.instance.hostname要与defaultZone里的地址保持一致各个eureka的spring.application.name相同 拷贝eureka服务分别命名eureka-ha-peer1和eureka-ha-peer2 修改模块的pom.xml artifactIdeureka-ha-peer1/artifactId修改配置文件application.yml注意集群服务需要各个eureka的spring.application.name相同 server:port: ${EUREKA_PORT:8762} eureka:client:service-url:defaultZone: ${EUREKA_SERVER:http://${spring.security.user.name}:${spring.security.user.password}peer1:8762/eureka/,http://${spring.security.user.name}:${spring.security.user.password}peer2:8763/eureka/}fetch-registry: trueinstance:instance-id: ${eureka.instance.hostname}:${server.port}hostname: peer1 spring:security:user:name: ${EUREKA_USER:admin}password: ${EUREKA_PASS:admin}application:name: eureka-cluster设置hosts文件 127.0.0.1 peer1 peer2服务提供者若想连接高可用的eureka需要修改 defaultZone: http://${EUREKA_USER:admin}:${EUREKA_PASS:admin}peer1:8762/eureka/,http://${EUREKA_USER:admin}:${EUREKA_PASS:admin}peer2:8763/eureka/ k8s交付 分析 高可用互相注册但是需要知道对方节点的地址。k8s中pod ip是不固定的如何将高可用的eureka服务使用k8s交付 方案一创建三个Deployment三个Service 方案二使用statefulset管理 eureka-statefulset.yaml # eureka-statefulset.yaml apiVersion: apps/v1 kind: StatefulSet metadata:name: eureka-clusternamespace: dev spec:serviceName: eurekareplicas: 3selector:matchLabels:app: eureka-clustertemplate:metadata:labels:app: eureka-clusterspec:containers:- name: eurekaimage: 172.21.51.67:5000/spring-cloud/eureka-cluster:v1ports:- containerPort: 8761resources:requests:memory: 400Micpu: 50mlimits:memory: 2Gicpu: 2000menv:- name: MY_POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name- name: JAVA_OPTSvalue: -XX:UnlockExperimentalVMOptions-XX:UseCGroupMemoryLimitForHeap-XX:MaxRAMFraction2-XX:CICompilerCount8-XX:ActiveProcessorCount8-XX:UseG1GC-XX:AggressiveOpts-XX:UseFastAccessorMethods-XX:UseStringDeduplication-XX:UseCompressedOops-XX:OptimizeStringConcat- name: EUREKA_SERVERvalue: http://admin:admineureka-cluster-0.eureka:8761/eureka/,http://admin:admineureka-cluster-1.eureka:8761/eureka/,http://admin:admineureka-cluster-2.eureka:8761/eureka/- name: EUREKA_INSTANCE_HOSTNAMEvalue: ${MY_POD_NAME}.eureka- name: EUREKA_PORTvalue: 8761eureka-headless-service.yaml apiVersion: v1 kind: Service metadata:name: eurekanamespace: devlabels:app: eureka spec:ports:- port: 8761name: eurekaclusterIP: Noneselector:app: eureka-cluster想通过ingress访问eureka需要使用有头服务 apiVersion: v1 kind: Service metadata:name: eureka-ingressnamespace: devlabels:app: eureka-cluster spec:ports:- port: 8761name: eureka-clusterselector:app: eureka-cluster --- apiVersion: extensions/v1beta1 kind: Ingress metadata:name: eureka-clusternamespace: dev spec:rules:- host: eureka-cluster.luffy.comhttp:paths:- backend:serviceName: eureka-ingressservicePort: 8761path: / status:loadBalancer: {}使用StatefulSet管理有状态服务 使用StatefulSet创建多副本pod的情况 apiVersion: apps/v1 kind: StatefulSet metadata:name: nginx-statefulsetlabels:app: nginx-sts spec:replicas: 3serviceName: nginxselector:matchLabels:app: nginx-ststemplate:metadata:labels:app: nginx-stsspec:containers:- name: nginximage: nginx:alpineports:- containerPort: 80无头服务Headless Service kind: Service apiVersion: v1 metadata:name: nginx spec:selector:app: nginx-stsports:- protocol: TCPport: 80targetPort: 80clusterIP: None$ kubectl -n spring exec -ti nginx-statefulset-0 sh / # curl nginx-statefulset-2.nginx接入CICD流程 所需的文件: 在pom.xml中重写jar包名称 finalName${project.artifactId}/finalNameDockerfile FROM openjdk:8-jdk-alpine ADD target/eureka.jar app.jar ENV JAVA_OPTS CMD [ sh, -c, java $JAVA_OPTS -jar /app.jar ]Jenkinsfile Library(luffy-devops) _pipeline {agent { label jnlp-slave}options {timeout(time: 20, unit: MINUTES)gitLabConnection(gitlab)}environment {IMAGE_REPO 172.21.51.67:5000/spring-cloud/eureka-clusterIMAGE_CREDENTIAL credential-registryDINGTALK_CREDS credentials(dingTalk)PROJECT eureka-cluster}stages {stage(checkout) {steps {container(tools) {checkout scm}}}stage(mvn-package) {steps {container(tools) {script{sh mvn clean package}}}}stage(CI){failFast trueparallel {stage(Unit Test) {steps {echo Unit Test Stage Skip...}}stage(Code Scan) {steps {container(tools) {script {devops.scan().start()}}}}}}stage(docker-image) {steps {container(tools) {script{devops.docker(${IMAGE_REPO},${GIT_COMMIT},IMAGE_CREDENTIAL).build().push()}}}}stage(deploy) {steps {container(tools) {script{devops.deploy(deploy,false,deploy/statefulset.yaml).start()}}}}}post {success {script{devops.notificationSuccess(PROJECT,dingTalk)}}failure {script{devops.notificationFailure(PROJECT,dingTalk)}}} }sonar-project.properties sonar.projectKeyeureka-cluster sonar.projectNameeureka-cluster # if you want disabled the DTD verification for a proxy problem for example, true by default # JUnit like test report, default value is test.xml sonar.sourcessrc/main/java sonar.languagejava sonar.testssrc/test/java sonar.java.binariestarget/classes模板化k8s资源清单 # eureka-statefulset.yaml apiVersion: apps/v1 kind: StatefulSet metadata:name: eureka-clusternamespace: {{NAMESPACE}} spec:serviceName: eurekareplicas: 3selector:matchLabels:app: eureka-clustertemplate:metadata:labels:app: eureka-clusterspec:containers:- name: eurekaimage: {{IMAGE_URL}} ...维护新组件的ingress: $ kubectl -n dev edit configmap devops-config ...INGRESS_EUREKA: eureka.luffy.com ...部署k8s集群时将eureka的集群地址通过参数的形式传递到pod内部因此本地开发时直接按照单点模式进行 server:port: ${EUREKA_PORT:8761} eureka:client:service-url:defaultZone: ${EUREKA_SERVER:http://${spring.security.user.name}:${spring.security.user.password}localhost:8761/eureka/}fetch-registry: trueregister-with-eureka: trueinstance:instance-id: ${eureka.instance.hostname}:${server.port}hostname: ${EUREKA_INSTANCE_HOSTNAME:localhost}prefer-ip-address: true spring:security:user:name: ${EUREKA_USER:admin}password: ${EUREKA_PASS:admin}application:name: eureka-cluster提交项目 创建develop分支CICD部署开发环境 停掉eureka-ha 微服务间调用 服务提供者 前面已经将用户服务注册到了eureka注册中心但是还没有暴漏任何API给服务消费者调用。 新建controller类 package com.luffy.userservice.controller;import com.luffy.userservice.entity.User; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;import java.util.Random;RestController public class UserController {GetMapping(/user)public String getUserService(){return this is user-service;}GetMapping(/user-nums)public Integer getUserNums(){return new Random().nextInt(100);}//{id: 123, name: 张三, age: 20, sex: male}GetMapping(/user/{id})public User getUserInfo(PathVariable(id) int id){User user new User();user.setId(id);user.setAge(20);user.setName(zhangsan);user.setSex(male);return user;} }实体类User.java package com.luffy.userservice.entity;public class User {private int id;private String name;private int age;private String sex;public int getAge() {return age;}public int getId() {return id;}public String getName() {return name;}public String getSex() {return sex;}public void setAge(int age) {this.age age;}public void setId(int id) {this.id id;}public void setName(String name) {this.name name;}public void setSex(String sex) {this.sex sex;} }application.yml 增加从环境变量中读取EUREKA_SERVER和EUREKA_INSTANCE_HOSTNAME配置 server:port: 7000 eureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://admin:adminlocalhost:8761/eureka/}instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: ${INSTANCE_HOSTNAME:user-service} spring:application:name: user-serviceCICD持续交付服务提供者 deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:name: user-servicenamespace: {{NAMESPACE}} spec:replicas: 1selector:matchLabels:app: user-servicetemplate:metadata:labels:app: user-servicespec:containers:- name: user-serviceimage: {{IMAGE_URL}}imagePullPolicy: IfNotPresentports:- containerPort: 7000resources:requests:memory: 400Micpu: 50mlimits:memory: 2Gicpu: 2000menv:- name: JAVA_OPTSvalue: -XX:UnlockExperimentalVMOptions-XX:UseCGroupMemoryLimitForHeap-XX:MaxRAMFraction2-XX:CICompilerCount8-XX:ActiveProcessorCount8-XX:UseG1GC-XX:AggressiveOpts-XX:UseFastAccessorMethods-XX:UseStringDeduplication-XX:UseCompressedOops-XX:OptimizeStringConcat- name: EUREKA_SERVERvalue: http://admin:admineureka-cluster-0.eureka:8761/eureka/,http://admin:admineureka-cluster-1.eureka:8761/eureka/,http://admin:admineureka-cluster-2.eureka:8761/eureka/- name: INSTANCE_HOSTNAMEvalueFrom:fieldRef:fieldPath: metadata.nameservice.yaml apiVersion: v1 kind: Service metadata:name: user-servicenamespace: {{NAMESPACE}} spec:ports:- port: 7000protocol: TCPtargetPort: 7000selector:app: user-servicesessionAffinity: Nonetype: ClusterIP status:loadBalancer: {}ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata:name: user-servicenamespace: {{NAMESPACE}} spec:rules:- host: {{INGRESS_USER_SERVICE}}http:paths:- backend:serviceName: user-serviceservicePort: 7000path: / status:loadBalancer: {}ingress配置 $ kubectl -n dev edit configmap devops-config ... data:INGRESS_MYBLOG: blog-dev.luffy.comINGRESS_SPRINGBOOTDEMO: springboot-dev.luffy.comINGRESS_USER_SERVICE: user-service-dev.luffy.comNAMESPACE: dev ...Jenkinsfile Library(luffy-devops) _pipeline {agent { label jnlp-slave}options {timeout(time: 20, unit: MINUTES)gitLabConnection(gitlab)}environment {IMAGE_REPO 172.21.51.67:5000/spring-cloud/user-serviceIMAGE_CREDENTIAL credential-registryDINGTALK_CREDS credentials(dingTalk)PROJECT user-service}stages {stage(checkout) {steps {container(tools) {checkout scm}}}stage(mvn-package) {steps {container(tools) {script{sh mvn clean package}}}}stage(CI){failFast trueparallel {stage(Unit Test) {steps {echo Unit Test Stage Skip...}}stage(Code Scan) {steps {container(tools) {script {devops.scan().start()}}}}}}stage(docker-image) {steps {container(tools) {script{devops.docker(${IMAGE_REPO},${GIT_COMMIT},IMAGE_CREDENTIAL).build().push()}}}}stage(deploy) {steps {container(tools) {script{devops.deploy(deploy,true,deploy/deployment.yaml).start()}}}}}post {success {script{devops.notificationSuccess(PROJECT,dingTalk)}}failure {script{devops.notificationFailure(PROJECT,dingTalk)}}} }pom.xml finalName${project.artifactId}/finalNameDockerfile FROM openjdk:8-jdk-alpine COPY target/user-service.jar app.jar ENV JAVA_OPTS CMD [ sh, -c, java $JAVA_OPTS -jar /app.jar ]sonar-project.properties sonar.projectKeyuser-service sonar.projectNameuser-service # if you want disabled the DTD verification for a proxy problem for example, true by default # JUnit like test report, default value is test.xml sonar.sourcessrc/main/java sonar.languagejava sonar.testssrc/test/java sonar.java.binariestarget/classes创建user-service项目提交代码 git init git remote add origin http://gitlab.luffy.com/luffy-spring-cloud/user-service.git git add . git commit -m Initial commit git push -u origin master# 提交到develop分支 git checkout -b develop git push -u origin develop创建Jenkins任务测试自动部署 访问http://user-service-dev.luffy.com/ 验证 服务消费者 RestTemplate 在Spring中提供了RestTemplate。RestTemplate是Spring提供的用于访问Rest服务的客户端。而在SpringCloud中也是使用此服务进行服务调用的。 创建bill-service模块 新的模块初始化三部曲 pom.xml启动类配置文件 pom.xml 添加如下内容: dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependency全量内容如下: ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.6.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.luffy/groupIdartifactIdbill-service/artifactIdversion0.0.1-SNAPSHOT/versionnamebill-service/namedescriptionbill-service/descriptionpropertiesjava.version1.8/java.versionspring-cloud.versionHoxton.SR9/spring-cloud.version/propertiesdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-actuator/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scopeexclusionsexclusiongroupIdorg.junit.vintage/groupIdartifactIdjunit-vintage-engine/artifactId/exclusion/exclusions/dependency/dependenciesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementbuildfinalName${project.artifactId}/finalName!--打jar包去掉版本号--pluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/projectBillServiceApplication package com.luffy.billservice;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;SpringBootApplication EnableDiscoveryClient public class BillServiceApplication {public static void main(String[] args) {SpringApplication.run(BillService.class, args);} }application.yml server:port: 7001 eureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://admin:adminlocalhost:8761/eureka/}instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: ${INSTANCE_HOSTNAME:bill-service} spring:application:name: bill-serviceBillController package com.luffy.billservice.controller;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;RestController public class BillController {Beanpublic RestTemplate restTemplate() {return new RestTemplate();}Autowiredprivate RestTemplate restTemplate;GetMapping(/bill/user)public String getUserInfo(){return restTemplate.getForObject(http://localhost:7000/user, String.class);} }问题 服务调用采用指定IPPort方式注册中心未使用多个服务负载均衡 使用注册中心实现服务调用 修改BillController package com.luffy.billservice.controller;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;RestController public class BillController {BeanLoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}Autowiredprivate RestTemplate restTemplate;GetMapping(/bill/user)public String getUserInfo(){return restTemplate.getForObject(http://user-service/user, String.class);} }访问测试 总体来说就是通过为加入LoadBalanced注解的RestTemplate添加一个请求拦截器在请求前通过拦截器获取真正的请求地址最后进行服务调用。 友情提醒若被LoadBalanced注解的RestTemplate访问正常的服务地址如http://127.0.0.1:8080/hello时是会提示无法找到此服务的。 具体原因serverid必须是我们访问的服务名称 当我们直接输入ip的时候获取的server是null就会抛出异常。 如果想继续调用可以通过如下方式 package com.luffy.billservice.controller;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;RestController public class BillController {BeanLoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}Autowiredprivate RestTemplate restTemplate;Bean(normalRestTemplate)public RestTemplate normalRestTemplate() {return new RestTemplate();}AutowiredQualifier(normalRestTemplate)RestTemplate normalRestTemplate;GetMapping(/service/user)public String getUserInfo(){return restTemplate.getForObject(http://user-service/user, String.class);}GetMapping(/normal)public String normal() {return normalRestTemplate.getForObject(http://localhost:7000/user, String.class);} }Ribbon 负载均衡 再启动一个user-service-instance2复制user-service项目 修改user-service-instance2的application.yml的server.port server:port: 7002 eureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://admin:adminpeer1:8761/eureka/}instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: ${INSTANCE_HOSTNAME:user-service} spring:application:name: user-service修改user-service-instance2的UserController.java为了可以区分是哪个服务提供者的实例提供的服务 package com.luffy.userservice.controller;import com.luffy.userservice.entity.User; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;import java.util.Random;RestController public class UserController {GetMapping(/user)public String getUserService(){return this is user-service-instance2;}GetMapping(/user-nums)public Integer getUserNums(){return new Random().nextInt(100);}//{id: 123, name: 张三, age: 20, sex: male}GetMapping(/user/{id})public User getUserInfo(PathVariable(id) int id){User user new User();user.setId(id);user.setAge(20);user.setName(zhangsan);user.setSex(male);return user;} }访问bill-service查看调用结果默认是轮询策略 Spring Cloud Ribbon是一个基于Http和TCP的客服端负载均衡工具它是基于Netflix Ribbon实现的。与Eureka配合使用时Ribbon可自动从Eureka Server (注册中心)获取服务提供者地址列表并基于负载均衡算法通过在客户端中配置ribbonServerList来设置服务端列表去轮询访问以达到均衡负载的作用。 eureka-client中包含了ribbon的包所以不需要单独引入 如何修改调用策略 代码中指定rule的规则配置文件配置 在bill-service中新建packagecom.luffy.rule注意不能被springboot扫描到不然规则就成了全局规则所有的ribbonclient都会应用到该规则。 package com.luffy.rule;import com.netflix.loadbalancer.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;Configuration public class RandomConfiguration {Beanpublic IRule ribbonRule() {// new BestAvailableRule();// new WeightedResponseTimeRule();return new RandomRule();} }修改BillController import com.luffy.rule.RandomConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.ribbon.RibbonClient;SpringBootApplication EnableDiscoveryClient RibbonClient(name user-service, configuration RandomConfiguration.class) public class BillServiceApplication {public static void main(String[] args) {SpringApplication.run(BillServiceApplication.class, args);} }配置文件方式 https://docs.spring.io/spring-cloud-netflix/docs/2.2.5.RELEASE/reference/html/#customizing-the-ribbon-client-by-setting-properties 注释掉代码 package com.luffy.ticket;import com.luffy.rule.RandomConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.ribbon.RibbonClient;SpringBootApplication EnableDiscoveryClient //RibbonClient(name USER-SERVICE, configuration RandomConfiguration.class) public class TicketApplication {public static void main(String[] args) {SpringApplication.run(TicketApplication.class, args);} }修改配置文件 server:port: ${SERVER_PORT:9000}spring:application:name: bill-serviceeureka:client:service-url:defaultZone: ${EUREKA_SERVER:http://admin:adminpeer1:8762/eureka/,http://admin:adminpeer2:8763/eureka/}instance:prefer-ip-address: trueinstance-id: ${spring.cloud.client.ip-address}:${server.port} user-service:ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule声明式服务Feign 从上一章节我们知道当我们要调用一个服务时需要知道服务名和api地址这样才能进行服务调用服务少时这样写觉得没有什么问题但当服务一多接口参数很多时上面的写法就显得不够优雅了。所以接下来来说说一种更好更优雅的调用服务的方式Feign。 Feign是Netflix开发的声明式、模块化的HTTP客户端。Feign可帮助我们更好更快的便捷、优雅地调用HTTP API。 在Spring Cloud中使用Feign非常简单——创建一个接口并在接口上添加一些注解。Feign支持多种注释例如Feign自带的注解或者JAX-RS注解等 Spring Cloud对Feign进行了增强使Feign支持了Spring MVC注解并整合了Ribbon和 Eureka,从而让Feign 的使用更加方便。只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。 https://github.com/OpenFeign/feign 对bill-service项目添加openfeign的依赖引入 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency启动类中引入Feign注解 package com.luffy.billservice;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients;SpringBootApplication EnableDiscoveryClient EnableFeignClients public class BillServiceApplication {public static void main(String[] args) {SpringApplication.run(BillServiceApplication.class, args);}}建立interface package com.luffy.billservice.interfaces;import com.luffy.billservice.entity.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;FeignClient(nameuser-service) public interface UserServiceCli {GetMapping(/user)public String getUserService();GetMapping(/user/{id})public User getUserInfo(PathVariable(id) int id); }拷贝User类到当前项目 package com.luffy.billservice.entity;public class User {private int id;private String name;private int age;private String sex;public int getAge() {return age;}public int getId() {return id;}public String getName() {return name;}public String getSex() {return sex;}public void setAge(int age) {this.age age;}public void setId(int id) {this.id id;}public void setName(String name) {this.name name;}public void setSex(String sex) {this.sex sex;} }修改BillController package com.luffy.billservice.controller;import com.luffy.billservice.entity.User; import com.luffy.billservice.interfaces.UserServiceCli; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;RestController public class BillController {Autowiredprivate UserServiceCli userServiceCli;GetMapping(/bill/user)public String getUserInfo(){return userServiceCli.getUserService();}GetMapping(/bill/user/{id})public User getUserInfo(PathVariable(id) int id){return userServiceCli.getUserInfo(id);//return restTemplate.getForObject(http://USER-SERVICE/user/ id, String.class);} }CICD持续交付服务消费者 拷贝user-service的交付文件替换如下 user-service - bill-service7000 - 7001INGRESS_USER_SERVICE - INGRESS_BILL_SERVICE $ kubectl -n dev edit configmap devops-config ... data:INGRESS_MYBLOG: blog-dev.luffy.comINGRESS_SPRINGBOOTDEMO: springboot-dev.luffy.comINGRESS_USER_SERVICE: user-service-dev.luffy.comINGRESS_BILL_SERVICE: user-service-dev.luffy.comNAMESPACE: dev ...创建develop分支提交代码到gitlab仓库验证持续交付 前面主要讲解了下服务消费者如何利用原生、ribbon、fegin三种方式进行服务调用的其实每种调用方式都是使用restTemplate来进行调用的只是有些进行了增强目的是使用起来更简单高效。 Hystrix 断路器 为什么需要断路器 A作为服务提供者B为A的服务消费者C和D是B的服务消费者。A不可用引起了B的不可用并将不可用像滚雪球一样放大到C和D时雪崩效应就形成了。 因此需要实现一种机制可以做到自动监控服务状态并根据调用情况进行自动处理。 记录时间周期内服务调用失败次数维护断路器的打开、关闭、半开三种状态提供fallback机制 修改bill-service项目 pom.xml dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-hystrix/artifactId/dependencyapplication.xml feign:hystrix:enabled: true启动类添加注解EnableCircuitBreaker package com.luffy.billservice;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients;SpringBootApplication EnableDiscoveryClient EnableFeignClients EnableCircuitBreaker public class BillServiceApplication {public static void main(String[] args) {SpringApplication.run(BillServiceApplication.class, args);}}UserServiceCli.java package com.luffy.bill.interfaces;import com.luffy.bill.entity.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;FeignClient(nameuser-service, fallback UserServiceFallbackImpl.class) public interface UserServiceCli {GetMapping(/user)public String getUserService();GetMapping(/user/{id})public User getUserInfo(PathVariable(id) int id); }UserServiceFallbackImpl.java package com.luffy.billservice.interfaces;import com.luffy.billservice.entity.User; import org.springframework.stereotype.Component;Component(fallback) public class UserServiceFallbackImpl implements UserServiceCli{Overridepublic String getUserService() {return fallback user service;}Overridepublic User getUserInfo(int id) {User user new User();user.setId(1);user.setName(feign-fallback);return user;} }停止user-service测试熔断及fallback。 Hystrix Dashboard 前面一章我们讲解了如何整合Hystrix。而在实际情况下使用了Hystrix的同时,还会对其进行实时的数据监控反馈各类指标数据。今天我们就将讲解下Hystrix Dashboard和Turbine.其中Hystrix Dashboard是一款针对Hystrix进行实时监控的工具通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据,监控单个实例内的指标情况。后者Turbine能够将多个实例指标数据进行聚合的工具。 在eureka注册中心处访问bill-service的服务actuator地址: http://192.168.136.1:7001/actuator/info 若访问不了,需要添加如下内容: 为服务消费者bill-service的pom.xml添加依赖: dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-actuator/artifactId/dependency修改application.yml配置: management:endpoints:web:exposure:include: *访问http://localhost:9000/actuator/hystrix.stream 即可访问到断路器的执行状态但是显示不太友好因此需要dashboard。 新建项目hystrix-dashboard Hystrix-dashboard(仪表盘)是一款针对Hystrix进行实时监控的工具通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据。 pom.xml引入依赖包 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.6.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.luffy/groupIdartifactIdhystrix-dashboard/artifactIdversion0.0.1-SNAPSHOT/versionnamehystrix-dashboard/namedescriptionhystrxi dashboard/descriptionpropertiesjava.version1.8/java.versionspring.cloud-versionHoxton.SR9/spring.cloud-version/propertiesdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-hystrix-dashboard/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scopeexclusionsexclusiongroupIdorg.junit.vintage/groupIdartifactIdjunit-vintage-engine/artifactId/exclusion/exclusions/dependency/dependenciesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring.cloud-version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project 启动类加上EnableHystrixDashboard注解 package com.luffy.hystrixdashboard;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;SpringBootApplication EnableHystrixDashboard public class HystrixDashboardApplication {public static void main(String[] args) {SpringApplication.run(HystrixDashboardApplication.class, args);}}application.yml #应用名称 server:port: 9696spring:application:name: hystrix-dashboard hystrix:dashboard:proxy-stream-allow-list: *访问localhost:9696/hystrix 实心圆它有颜色和大小之分分别代表实例的监控程度和流量大小。如上图所示它的健康度从绿色、黄色、橙色、红色递减。通过该实心圆的展示我们就可以在大量的实例中快速的发现故障实例和高压力实例。 曲线用来记录 2 分钟内流量的相对变化我们可以通过它来观察到流量的上升和下降趋势。 其他一些数量指标如下图所示 提交代码到gitlab仓库 Turbine hystrix只能实现单个微服务的监控可是一般项目中是微服务是以集群的形式搭建一个一个的监控不现实。而Turbine的原理是建立一个turbine服务并注册到eureka中并发现eureka上的hystrix服务。通过配置turbine会自动收集所需hystrix的监控信息最后通过dashboard展现以达到集群监控的效果。 简单来说就是通过注册到注册中心发现其他服务的hystrix服务然后进行聚合数据最后通过自身的端点输出到仪表盘上进行个性化展示。这我们就监控一个turbine应用即可当有新增的应用加入时我们只需要配置下turbine参数即可。 微服务网关 为什么需要网关 在微服务框架中每个对外服务都是独立部署的对外的api或者服务地址都不是不尽相同的。对于内部而言很简单通过注册中心自动感知即可。但我们大部分情况下服务都是提供给外部系统进行调用的不可能同享一个注册中心。同时一般上内部的微服务都是在内网的和外界是不连通的。而且就算我们每个微服务对外开放对于调用者而言调用不同的服务的地址或者参数也是不尽相同的这样就会造成消费者客户端的复杂性同时想想可能微服务可能是不同的技术栈实现的有的是http、rpc或者websocket等等也会进一步加大客户端的调用难度。所以一般上都有会有个api网关根据请求的url不同路由到不同的服务上去同时入口统一了还能进行统一的身份鉴权、日志记录、分流等操作。 网关的功能 减少api请求次数限流缓存统一认证降低微服务的复杂度支持混合通信协议(前端只和api通信其他的由网关调用)… Zuul实践 新建模块gateway-zuul,(spring cloud) pom.xml中需要引入zuul和eureka服务发现的依赖 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-zuul/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependency启动类添加注解 package com.luffy.gateway;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy;SpringBootApplication EnableZuulProxy EnableDiscoveryClient public class ZuulGatewayApplication {public static void main(String[] args) {SpringApplication.run(ZuulGatewayApplication.class, args);} }配置文件 server:port: 10000spring:application:name: gateway-zuuleureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://admin:adminlocalhost:8761/eureka/}instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: ${INSTANCE_HOSTNAME:gateway-zuul}启动后访问 http://localhost:10000/bill-service/bill/user/1 http://localhost:10000/user-service/user 通过如下方式配置短路径 zuul:routes:user-service: /users/**bill-service:path: /bill/**service-id: bill-servicehttp://localhost:10000/users/user/1--- http://localhost:7000/user/2 http://localhost:10000/user-service/user/1http://localhost:10000/bill/service/user/2 ---http://localhost:7001/service/user/2 http://localhost:10000/bill-service/service/user/2zuul如何指定对外暴漏api的path如 所有的api都是这样http://zuul-host:zuul-port/apis/可以添加zuul.prefix/apis 配置一下配置文件 management:endpoints:web:exposure:include: *可以访问到zuul的route列表 http://localhost:10000/actuator/routes/ 添加details可以访问到详细信息 { /apis/users/**: user-service, /apis/bill/**: bill-service, /apis/bill-service/**: bill-service, /apis/user-service/**: user-service }提交代码到代码仓库 集中配置中心 Spring Cloud Config 配置中心提供了一个中心化的外部配置默认使用git存储配置信息这样就可以对配置信息进行版本管理。 实践 创建代码仓库configure-repo用于集中存储配置文件创建项目config-server用于接受各项目的连接提供配置文件读取服务修改user-service服务验证通过config-server读取集中配置库中的配置文件 代码仓库 新建gitlab项目http://gitlab.luffy.com/luffy-spring-cloud/configure-repo.git 准备配置文件 configs/common-dev.yml datasource:url: jdbc:mysql://mysql-dev:3306/driverClassName: com.mysql.jdbc.Driverusername: xxxpassword: xxxxxx luffy: cityconfigs/user-service-dev.yml logging:level:org.springframework.cloud: debug env: devconfigs/common-test.yml env: test提交代码到master分支 新建项目config-server 修改pom.xmlspringboot和springcloud的版本 springboot 版本 version2.3.6.RELEASE/versionspring-cloud 版本 propertiesjava.version1.8/java.versionspring-cloud.versionHoxton.SR9/spring-cloud.version/properties添加config-server 的依赖 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-config-server/artifactId/dependency修改application.yml server:port: 8088spring:application:name: config-serverprofiles:active: gitcloud:config:server:git:uri: http://gitlab.luffy.com/luffy-spring-cloud/configure-repo.gitusername: ${GIT_USER:root}password: ${GIT_PSW:1qaz2wsx}default-label: mastersearch-paths: configs#native:# searchLocations: classpath:/configs/{profile}修改启动类添加注解 package com.luffy.configserver;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer;SpringBootApplication EnableConfigServer public class ConfigServerApplication {public static void main(String[] args) {SpringApplication.run(ConfigServerApplication.class, args);}}启动config-server,访问: http://localhost:8088/common/dev http://localhost:8088/user-service/dev修改user-service服务从config-server读取配置 添加使用统一配置中心的依赖 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-config/artifactId/dependency新建bootstrap.yml不能放在application.yml中因为bootstrap的加载早于应用程序bean启动的加载因此删掉application.yml直接使用bootstrap.yml server:port: 7000eureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://admin:adminlocalhost:8761/eureka/}instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: ${INSTANCE_HOSTNAME:user-service} spring:application:name: user-servicecloud:config:uri: http://localhost:8088profile: dev #当前读取dev环境的配置name: user-service, common # 从user-service-dev.yml,common-dev.yml中读取 新建ValueController.java package com.luffy.userservice.controller;import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;RestController public class ValueController {Value(${env})private String env;Value(${datasource.url})private String datasource;Value(${spring.application.name})private String applicationName;GetMapping(/value/env)public String getValueEnv(){return current env is env;}GetMapping(/value/application)public String getValueApplication(){return current env is applicationName;}GetMapping(/value/datasource)public Object getDatasource(){return datasource;}}访问如下页面进行验证 $ localhost:7000/value/env $ localhost:7000/value/application $ localhost:7000/value/datasource高可用 config-server多个实例如何配置客户端 config-server 作为服务提供者注册到eureka服务注册中心user-service配置从注册中心获取config-server的服务 config-server注册到服务注册中心 pom.xml添加eureka依赖包 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencyapplication.yml中连接服务注册中心 eureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://admin:adminlocalhost:8761/eureka/}instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: ${INSTANCE_HOSTNAME:config-server}启动类添加注解 EnableDiscoveryClient修改user-service从注册中心发现服务 修改bootstrap.yml server:port: 7000spring:cloud:config:profile: devdiscovery:enabled: trueservice-id: config-servername: user-service, commoneureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://admin:adminpeer1:8761/eureka/}instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: ${INSTANCE_HOSTNAME:user-service}客户端配置刷新 配置中心的配置变动后客户端如何获取最新的配置。 修改ValueController.java添加注解 RestController RefreshScope public class ValueController 添加actuator包 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-actuator/artifactId/dependency开放显示所有管理接口 management:endpoints:web:exposure:include: *重启user-service 修改configure-repo中的配置并提交访问http://localhost:7000/value/env 执行刷新 $ curl -XPOST http://localhost:7000/actuator/refresh再次访问http://localhost:7000/value/env 调用链路追踪 介绍 服务追踪的追踪单元是从客户发起请求request抵达被追踪系统的边界开始到被追踪系统向客户返回响应response为止的过程称为一个 trace。每个 trace 中会调用若干个服务为了记录调用了哪些服务以及每次调用的消耗时间等信息在每次调用服务时埋入一个调用记录称为一个 span。这样若干个有序的 span 就组成了一个 trace。在系统向外界提供服务的过程中会不断地有请求和响应发生也就会不断生成 trace把这些带有 span 的 trace 记录下来就可以描绘出一幅系统的服务拓扑图。附带上 span 中的响应时间以及请求成功与否等信息就可以在发生问题的时候找到异常的服务根据历史数据还可以从系统整体层面分析出哪里性能差定位性能优化的目标。 Spring Cloud Sleuth 为服务之间调用提供链路追踪。通过 Sleuth 可以很清楚的了解到一个服务请求经过了哪些服务每个服务处理花费了多长。从而让我们可以很方便的理清各微服务间的调用关系。此外 Sleuth 可以帮助我们 耗时分析: 通过 Sleuth 可以很方便的了解到每个采样请求的耗时从而分析出哪些服务调用比较耗时; 链路优化: 对于调用比较频繁的服务可以针对这些服务实施一些优化措施。 可视化错误: 对于程序未捕捉的异常可以通过集成 Zipkin 服务界面上看到; Spring Cloud Sleuth 可以结合 Zipkin将信息发送到 Zipkin利用 Zipkin 的存储来存储信息利用 Zipkin UI 来展示数据。 https://zipkin.io/pages/quickstart 启动zipkin apiVersion: apps/v1 kind: Deployment metadata:name: zipkinnamespace: dev spec:replicas: 1selector:matchLabels:app: zipkintemplate:metadata:labels:app: zipkinspec:containers:- name: zipkinimage: openzipkin/zipkin:2.22imagePullPolicy: IfNotPresentports:- containerPort: 9411resources:requests:memory: 400Micpu: 50mlimits:memory: 2Gicpu: 2000m --- apiVersion: v1 kind: Service metadata:name: zipkinnamespace: dev spec:ports:- port: 9411protocol: TCPtargetPort: 9411selector:app: zipkinsessionAffinity: Nonetype: ClusterIP status:loadBalancer: {} --- apiVersion: extensions/v1beta1 kind: Ingress metadata:name: zipkinnamespace: dev spec:rules:- host: zipkin.luffy.comhttp:paths:- backend:serviceName: zipkinservicePort: 9411path: / status:loadBalancer: {}实践 分别对bill-service和user-service进行改造 pom.xml中添加 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-zipkin/artifactId/dependencyapplication.yml spring:zipkin:base-url: http://zipkin.luffy.com # zipkin服务器的地址sender:type: web # 设置使用http的方式传输数据sleuth:sampler:probability: 1 # 设置抽样采集为100%默认为0.1即10% logging:level:org.springframework.cloud: debug访问zuul网关的接口http://localhost:10000/apis/bill-service/bill/user/2 2020-11-14 19:28:49.274 DEBUG [bill-service,949aa3570daa1031,43ea952f1e5e36eb,true] 36852 — [-user-service-6] c.s.i.w.c.f.TraceLoadBalancerFeignClient : Before send bill-service,949aa3570daa1031,43ea952f1e5e36eb,true说明 bill-service 服务名称949aa3570daa1031 是TranceId一条链路中只有一个TranceId43ea952f1e5e36eb则是spanId链路中的基本工作单元idtrue表示是否将数据输出到其他服务true则会把信息输出到其他可视化的服务上观察 SpringBoot Admin监控 新建项目springboot-admin pom.xml ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.6.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.luffy/groupIdartifactIdspringboot-admin/artifactIdversion0.0.1-SNAPSHOT/versionnamespringboot-admin/namedescriptionDemo project for Spring Boot/descriptionpropertiesjava.version1.8/java.versionspring-cloud.versionHoxton.SR9/spring-cloud.version/propertiesdependenciesdependencygroupIdde.codecentric/groupIdartifactIdspring-boot-admin-starter-server/artifactIdversion2.2.1/version/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scopeexclusionsexclusiongroupIdorg.junit.vintage/groupIdartifactIdjunit-vintage-engine/artifactId/exclusion/exclusions/dependency/dependenciesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project配置文件 server:port: 8769spring:application:name: springboot-admineureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://admin:adminlocalhost:8761/eureka/}instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: ${INSTANCE_HOSTNAME:springboot-admin}启动类 package com.luffy.springbootadmin;import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;SpringBootApplication EnableDiscoveryClient EnableAdminServer public class SpringbootAdminApplication {public static void main(String[] args) {SpringApplication.run(SpringbootAdminApplication.class, args);}}客户端所有注册到eureka的服务添加依赖即可 dependencygroupIdde.codecentric/groupIdartifactIdspring-boot-admin-starter-client/artifactIdversion2.2.1/version/dependency小结 伴随着业务场景复杂度的提高单体架构应用弊端显现微服务的思想逐步盛行微服务架构带来诸多便捷的同时也带来了很多问题最主要的是多个微服务的服务治理服务发现、调用、负载均衡、跟踪为了解决服务治理问题出现了微服务框架Dubbo、Spring Cloud等Spring Cloud是一个大的生态基于Java语言封装了一系列的工具方便业务直接使用来解决上述服务治理相关的问题Spring Cloud Netflix 体系下提供了eureka、ribbon、feign、hystrix、zuul等工具结合spring cloud sleuth合zipkin实现服务跟踪SpringBoot是微服务的开发框架通过maven与Spring Cloud生态中的组件集成极大方便了java应用程序的交付 https://blog.csdn.net/smallsunl/article/details/78778790 问题 无论是Dubbo还是SpringCloud均属于Java语言体系下的产物跨语言没法共用同时通过走了一遍内部集成的过程可以清楚的发现服务治理过程中各模块的集成均需要对原始业务逻辑形成侵入。在kubernetes的生态下已经与生俱来带了很多好用的功能自动服务发现与负载均衡服务治理的根本其实是网络节点通信的治理因此以istio为代表的第二代服务治理平台开始逐步兴起 ng Boot propertiesjava.version1.8/java.versionspring-cloud.versionHoxton.SR9/spring-cloud.version /propertiesdependenciesdependencygroupIdde.codecentric/groupIdartifactIdspring-boot-admin-starter-server/artifactIdversion2.2.1/version/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-netflix-eureka-client/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scopeexclusionsexclusiongroupIdorg.junit.vintage/groupIdartifactIdjunit-vintage-engine/artifactId/exclusion/exclusions/dependency /dependenciesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependency/dependencies /dependencyManagementbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins /build配置文件 server:port: 8769spring:application:name: springboot-admineureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://admin:adminlocalhost:8761/eureka/}instance:instance-id: ${eureka.instance.hostname}:${server.port}prefer-ip-address: truehostname: ${INSTANCE_HOSTNAME:springboot-admin}启动类 package com.luffy.springbootadmin;import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;SpringBootApplication EnableDiscoveryClient EnableAdminServer public class SpringbootAdminApplication {public static void main(String[] args) {SpringApplication.run(SpringbootAdminApplication.class, args);}}客户端所有注册到eureka的服务添加依赖即可 dependencygroupIdde.codecentric/groupIdartifactIdspring-boot-admin-starter-client/artifactIdversion2.2.1/version/dependency小结 伴随着业务场景复杂度的提高单体架构应用弊端显现微服务的思想逐步盛行微服务架构带来诸多便捷的同时也带来了很多问题最主要的是多个微服务的服务治理服务发现、调用、负载均衡、跟踪为了解决服务治理问题出现了微服务框架Dubbo、Spring Cloud等Spring Cloud是一个大的生态基于Java语言封装了一系列的工具方便业务直接使用来解决上述服务治理相关的问题Spring Cloud Netflix 体系下提供了eureka、ribbon、feign、hystrix、zuul等工具结合spring cloud sleuth合zipkin实现服务跟踪SpringBoot是微服务的开发框架通过maven与Spring Cloud生态中的组件集成极大方便了java应用程序的交付 https://blog.csdn.net/smallsunl/article/details/78778790 问题 无论是Dubbo还是SpringCloud均属于Java语言体系下的产物跨语言没法共用同时通过走了一遍内部集成的过程可以清楚的发现服务治理过程中各模块的集成均需要对原始业务逻辑形成侵入。在kubernetes的生态下已经与生俱来带了很多好用的功能自动服务发现与负载均衡服务治理的根本其实是网络节点通信的治理因此以istio为代表的第二代服务治理平台开始逐步兴起
http://www.zqtcl.cn/news/947126/

相关文章:

  • 第四章第二节网站建设的教学设计云南网站建设一度科技公司
  • php 搭建手机网站建e网app下载
  • 河北手机版建站系统价格微信怎么开店铺小程序
  • 中国建设教育网官网是什么网站潮州seo建站
  • 如何做个购物网站学校网站设计的目的
  • 建设部网站158号文件1688官网app
  • 临沂科技网站建设在线网页截图工具
  • 聊城网站推广软件简单网页制作训练
  • wordpress去除文章作者seo核心技术排名
  • 网站建设黄页免费观看wordpress所有文章
  • 企业整站优化沈阳建设学院
  • 网站怎么做弹框河北省建设注册中心网站首页
  • 大连哪里有手机自适应网站建设网站开发层次
  • 网站首页的浮窗怎么做美食网站程序
  • 淮北网站建设建设银行福州分行招聘网站
  • c 网站开发 pdf济南集团网站建设报价
  • 做网站找哪家公司好中国网络优化推广
  • 创建网站目录结构应遵循的方法dz旅游网站模板
  • 我看别人做系统就直接网站下载软件外贸物流流程
  • 手机微信网站南县网站定制
  • 做字幕网站重庆seo代理价格
  • 长春公司做网站找哪个公司好英文网站google推广
  • 潍坊网站建设方案推广官方网站如何建设
  • 设计网站的公司名称苏州建设人才网官网
  • 河南网站推广优化公司wordpress搭建vip下载站
  • 做网站拉客户有效吗网络宣传渠道
  • 制作深圳网站建设四川广安网站建设
  • 网站服务器服务商wordpress特效主题
  • 大型大型网站制作wordpress产品相册
  • 古董做推广哪个网站好租空间开网站