irpas技术客

基于SpringBoot,Spring Cloud的微服务框架,整合Gateway、Config、Ribbon、Hystrix、Eureka、OpenFeign

大大的周 607

基于SpringBoot,Spring Cloud的微服务框架,整合Gateway、Config、Ribbon、Hystrix、Eureka、OpenFeign 项目路径必要环境软件&版本&插件 项目结构模块说明 服务启动顺序具体实现关键服务pom父pomcommon pomgateway pomconfig pomeureka pom 实现EUREKA注册中心启动类配置文件(eureka服务不使用配置中心服务)启动EUREKA服务 实现CONFIG配置中心启动类配置文件启动CONFIG服务 实现GATEWAY网关服务启动类GATEWAY配置类GATEWAY配置文件启动GATEWAY服务 分别启动FALLBACK、MAINTAIN、WEBSOCKET服务这三个服务没啥特别,配置文件和GATEWAY一样,读取对应的配置文件就好调用GATEWAY对应路由规则的MAINTAIN服务提供的接口测试 最后启动ADMIN服务启动类访问 127.0.0.1:8887 测试 OpenFeign示例WEBSOCKET服务接口MAINTAIN调用WEBSOCKET接口 总结


项目路径

GitHub路径:https://github.com/wuyue930912/cloud-pet-hub.git Coding路径:https://e.coding.net/pethub/hub/pet-hub.git


必要环境
软件&版本&插件 数据库JDKSpringBootSpringCloud插件mariadb82.2.1.RELEASHoxton.SR1lombok

项目结构


模块说明 basecommoncloud-admincloud-configcloud-eurekacloud-fallbackcloud-gatewaycloud-maintaincloud-websocket数据库SQL & 各服务配置文件路径(配置中心读取配置文件路径)& maven所用setting.xml文件公共依赖Spring Boot Admin 服务配置中心服务注册中心服务服务熔断FallBack服务网关服务业务服务, 提供简单接口测试业务服务,提供WEBSOCKET服务

服务启动顺序 #mermaid-svg-ZBNusWRukQJiULeN .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-ZBNusWRukQJiULeN .label text{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN .node rect,#mermaid-svg-ZBNusWRukQJiULeN .node circle,#mermaid-svg-ZBNusWRukQJiULeN .node ellipse,#mermaid-svg-ZBNusWRukQJiULeN .node polygon,#mermaid-svg-ZBNusWRukQJiULeN .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-ZBNusWRukQJiULeN .node .label{text-align:center;fill:#333}#mermaid-svg-ZBNusWRukQJiULeN .node.clickable{cursor:pointer}#mermaid-svg-ZBNusWRukQJiULeN .arrowheadPath{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-ZBNusWRukQJiULeN .flowchart-link{stroke:#333;fill:none}#mermaid-svg-ZBNusWRukQJiULeN .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-ZBNusWRukQJiULeN .edgeLabel rect{opacity:0.9}#mermaid-svg-ZBNusWRukQJiULeN .edgeLabel span{color:#333}#mermaid-svg-ZBNusWRukQJiULeN .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-ZBNusWRukQJiULeN .cluster text{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-ZBNusWRukQJiULeN .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-ZBNusWRukQJiULeN text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-ZBNusWRukQJiULeN .actor-line{stroke:grey}#mermaid-svg-ZBNusWRukQJiULeN .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-ZBNusWRukQJiULeN .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-ZBNusWRukQJiULeN #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-ZBNusWRukQJiULeN .sequenceNumber{fill:#fff}#mermaid-svg-ZBNusWRukQJiULeN #sequencenumber{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN #crosshead path{fill:#333;stroke:#333}#mermaid-svg-ZBNusWRukQJiULeN .messageText{fill:#333;stroke:#333}#mermaid-svg-ZBNusWRukQJiULeN .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-ZBNusWRukQJiULeN .labelText,#mermaid-svg-ZBNusWRukQJiULeN .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-ZBNusWRukQJiULeN .loopText,#mermaid-svg-ZBNusWRukQJiULeN .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-ZBNusWRukQJiULeN .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-ZBNusWRukQJiULeN .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-ZBNusWRukQJiULeN .noteText,#mermaid-svg-ZBNusWRukQJiULeN .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-ZBNusWRukQJiULeN .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-ZBNusWRukQJiULeN .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-ZBNusWRukQJiULeN .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-ZBNusWRukQJiULeN .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN .section{stroke:none;opacity:0.2}#mermaid-svg-ZBNusWRukQJiULeN .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-ZBNusWRukQJiULeN .section2{fill:#fff400}#mermaid-svg-ZBNusWRukQJiULeN .section1,#mermaid-svg-ZBNusWRukQJiULeN .section3{fill:#fff;opacity:0.2}#mermaid-svg-ZBNusWRukQJiULeN .sectionTitle0{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN .sectionTitle1{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN .sectionTitle2{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN .sectionTitle3{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-ZBNusWRukQJiULeN .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN .grid path{stroke-width:0}#mermaid-svg-ZBNusWRukQJiULeN .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-ZBNusWRukQJiULeN .task{stroke-width:2}#mermaid-svg-ZBNusWRukQJiULeN .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN .taskText:not([font-size]){font-size:11px}#mermaid-svg-ZBNusWRukQJiULeN .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-ZBNusWRukQJiULeN .task.clickable{cursor:pointer}#mermaid-svg-ZBNusWRukQJiULeN .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-ZBNusWRukQJiULeN .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-ZBNusWRukQJiULeN .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-ZBNusWRukQJiULeN .taskText0,#mermaid-svg-ZBNusWRukQJiULeN .taskText1,#mermaid-svg-ZBNusWRukQJiULeN .taskText2,#mermaid-svg-ZBNusWRukQJiULeN .taskText3{fill:#fff}#mermaid-svg-ZBNusWRukQJiULeN .task0,#mermaid-svg-ZBNusWRukQJiULeN .task1,#mermaid-svg-ZBNusWRukQJiULeN .task2,#mermaid-svg-ZBNusWRukQJiULeN .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-ZBNusWRukQJiULeN .taskTextOutside0,#mermaid-svg-ZBNusWRukQJiULeN .taskTextOutside2{fill:#000}#mermaid-svg-ZBNusWRukQJiULeN .taskTextOutside1,#mermaid-svg-ZBNusWRukQJiULeN .taskTextOutside3{fill:#000}#mermaid-svg-ZBNusWRukQJiULeN .active0,#mermaid-svg-ZBNusWRukQJiULeN .active1,#mermaid-svg-ZBNusWRukQJiULeN .active2,#mermaid-svg-ZBNusWRukQJiULeN .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-ZBNusWRukQJiULeN .activeText0,#mermaid-svg-ZBNusWRukQJiULeN .activeText1,#mermaid-svg-ZBNusWRukQJiULeN .activeText2,#mermaid-svg-ZBNusWRukQJiULeN .activeText3{fill:#000 !important}#mermaid-svg-ZBNusWRukQJiULeN .done0,#mermaid-svg-ZBNusWRukQJiULeN .done1,#mermaid-svg-ZBNusWRukQJiULeN .done2,#mermaid-svg-ZBNusWRukQJiULeN .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-ZBNusWRukQJiULeN .doneText0,#mermaid-svg-ZBNusWRukQJiULeN .doneText1,#mermaid-svg-ZBNusWRukQJiULeN .doneText2,#mermaid-svg-ZBNusWRukQJiULeN .doneText3{fill:#000 !important}#mermaid-svg-ZBNusWRukQJiULeN .crit0,#mermaid-svg-ZBNusWRukQJiULeN .crit1,#mermaid-svg-ZBNusWRukQJiULeN .crit2,#mermaid-svg-ZBNusWRukQJiULeN .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-ZBNusWRukQJiULeN .activeCrit0,#mermaid-svg-ZBNusWRukQJiULeN .activeCrit1,#mermaid-svg-ZBNusWRukQJiULeN .activeCrit2,#mermaid-svg-ZBNusWRukQJiULeN .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-ZBNusWRukQJiULeN .doneCrit0,#mermaid-svg-ZBNusWRukQJiULeN .doneCrit1,#mermaid-svg-ZBNusWRukQJiULeN .doneCrit2,#mermaid-svg-ZBNusWRukQJiULeN .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-ZBNusWRukQJiULeN .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-ZBNusWRukQJiULeN .milestoneText{font-style:italic}#mermaid-svg-ZBNusWRukQJiULeN .doneCritText0,#mermaid-svg-ZBNusWRukQJiULeN .doneCritText1,#mermaid-svg-ZBNusWRukQJiULeN .doneCritText2,#mermaid-svg-ZBNusWRukQJiULeN .doneCritText3{fill:#000 !important}#mermaid-svg-ZBNusWRukQJiULeN .activeCritText0,#mermaid-svg-ZBNusWRukQJiULeN .activeCritText1,#mermaid-svg-ZBNusWRukQJiULeN .activeCritText2,#mermaid-svg-ZBNusWRukQJiULeN .activeCritText3{fill:#000 !important}#mermaid-svg-ZBNusWRukQJiULeN .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-ZBNusWRukQJiULeN g.classGroup text .title{font-weight:bolder}#mermaid-svg-ZBNusWRukQJiULeN g.clickable{cursor:pointer}#mermaid-svg-ZBNusWRukQJiULeN g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-ZBNusWRukQJiULeN g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-ZBNusWRukQJiULeN .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-ZBNusWRukQJiULeN .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-ZBNusWRukQJiULeN .dashed-line{stroke-dasharray:3}#mermaid-svg-ZBNusWRukQJiULeN #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN .commit-id,#mermaid-svg-ZBNusWRukQJiULeN .commit-msg,#mermaid-svg-ZBNusWRukQJiULeN .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-ZBNusWRukQJiULeN g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-ZBNusWRukQJiULeN g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-ZBNusWRukQJiULeN g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-ZBNusWRukQJiULeN .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-ZBNusWRukQJiULeN .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-ZBNusWRukQJiULeN .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-ZBNusWRukQJiULeN .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-ZBNusWRukQJiULeN .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-ZBNusWRukQJiULeN .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-ZBNusWRukQJiULeN .edgeLabel text{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-ZBNusWRukQJiULeN .node circle.state-start{fill:black;stroke:black}#mermaid-svg-ZBNusWRukQJiULeN .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-ZBNusWRukQJiULeN #statediagram-barbEnd{fill:#9370db}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-state .divider{stroke:#9370db}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-ZBNusWRukQJiULeN .note-edge{stroke-dasharray:5}#mermaid-svg-ZBNusWRukQJiULeN .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-ZBNusWRukQJiULeN .error-icon{fill:#522}#mermaid-svg-ZBNusWRukQJiULeN .error-text{fill:#522;stroke:#522}#mermaid-svg-ZBNusWRukQJiULeN .edge-thickness-normal{stroke-width:2px}#mermaid-svg-ZBNusWRukQJiULeN .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-ZBNusWRukQJiULeN .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-ZBNusWRukQJiULeN .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-ZBNusWRukQJiULeN .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-ZBNusWRukQJiULeN .marker{fill:#333}#mermaid-svg-ZBNusWRukQJiULeN .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-ZBNusWRukQJiULeN { color: rgba(0, 0, 0, 0.75); font: ; } EUREKA CONFIG GATEWAY FALLBACK MAINTAIN WEBSOCKET ADMIN
具体实现
关键服务pom
父pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://·piler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.1.Final</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <!--数据库驱动--> <dependency> <groupId>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <exclusions> <exclusion> <groupId>com.sun.jersey</groupId> <artifactId>jersey-client</artifactId> </exclusion> <exclusion> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> </exclusion> <exclusion> <groupId>com.sun.jersey.contribs</groupId> <artifactId>jersey-apache-client4</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> </project>
gateway pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://·piler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <exclusions> <exclusion> <groupId>com.sun.jersey</groupId> <artifactId>jersey-client</artifactId> </exclusion> <exclusion> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> </exclusion> <exclusion> <groupId>com.sun.jersey.contribs</groupId> <artifactId>jersey-apache-client4</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.2.4</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <excludes> <exclude>application.yml</exclude> <exclude>application-dev.yml</exclude> <exclude>application-prod.yml</exclude> </excludes> </resource> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <!-- 打包时所包含得文件 --> <includes> <include>application.yml</include> <include>application-${profileActive}.yml</include> </includes> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
config pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://·piler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.pet</groupId> <artifactId>common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.2.4</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <excludes> <exclude>application.yml</exclude> <exclude>application-dev.yml</exclude> <exclude>application-prod.yml</exclude> </excludes> </resource> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <!-- 打包时所包含得文件 --> <includes> <include>application.yml</include> <include>application-${profileActive}.yml</include> </includes> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
eureka pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://·piler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <!--eureka的服务端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.2.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <excludes> <exclude>application.yml</exclude> <exclude>application-dev.yml</exclude> <exclude>application-prod.yml</exclude> </excludes> </resource> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <!-- 打包时所包含得文件 --> <includes> <include>application.yml</include> <include>application-${profileActive}.yml</include> </includes> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
实现EUREKA注册中心
启动类 package com.pet; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 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; @SpringBootApplication @EnableEurekaServer @Slf4j public class EurekaStarter { public static void main(String[] args) { SpringApplication.run(EurekaStarter.class, args); log.info("start running eureka server : 【{}】", "http://127.0.0.1:8888"); } @EnableWebSecurity static class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().ignoringAntMatchers("/eureka/**"); super.configure(http); } } }
配置文件(eureka服务不使用配置中心服务) server: port: 8888 logging: file: path: D:\\LOG eureka: instance: hostname: 127.0.0.1 client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka server: eviction-interval-timer-in-ms: 6000 wait-time-in-ms-when-sync-empty: 6000 enable-self-preservation: true management: endpoints: web: exposure: include: "*" endpoint: health: show-details: ALWAYS spring: application: name: eureka security: user: name: eureka password: 设置EUREKA登录密码 roles: SUPERUSER jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8
启动EUREKA服务

启动成功访问 ip:8888端口出现下面页面说明配置成功。

实现CONFIG配置中心
启动类 package com.pet; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient @EnableConfigServer @Slf4j public class ConfigStarter { public static void main(String[] args) { SpringApplication.run(ConfigStarter.class, args); log.info("start running config server"); } }
配置文件 server: port: 8887 max-http-header-size: 20480 servlet: session: timeout: PT30M logging: file: path: D:\\LOG management: endpoints: web: exposure: include: "*" endpoint: health: show-details: ALWAYS eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://EUREKA账号:EUREKA密码@127.0.0.1:8888/eureka instance: prefer-ip-address: true ip-address: 127.0.0.1 instance-id: 127.0.0.1:${spring.application.name}:${server.port} feign: hystrix: enabled: true spring: cloud: config: server: git: uri: https://e.coding.net/pethub/hub/pet-hub.git ## git路径 username: git用户名 password: git密码 search-paths: base/script/config/application-* ## 配置文件存放具体路径 default-label: master ## 指定分支 application: name: config jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 datasource: url: jdbc:mariadb://127.0.0.1:3306/pet_hub?characterEncoding=utf-8&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8 driver-class-name: org.mariadb.jdbc.Driver username: root password: 数据库密码 type: com.alibaba.druid.pool.DruidDataSource initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true jpa: database-platform: org.hibernate.dialect.MySQL5InnoDBDialect database: MYSQL show-sql: true open-in-view: false servlet: multipart: enabled: true max-file-size: 10MB max-request-size: 10MB # REDIS 配置 redis: database: 0 host: 127.0.0.1 port: 6379 password: timeout: 6000 jedis: pool: max-active: 8 max-wait: -1 max-idle: 10 min-idle: 2
启动CONFIG服务

启动成功后,通过postman调用 127.0.0.1:8887/{服务名}/{版本号}接口,查看是否成功(第一次调用可能会比较慢,泡壶茶水儿,等一会儿~ 😄)。

出现下面响应,说明配置成功。

实现GATEWAY网关服务
启动类 package com.pet; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication @Slf4j public class GatewayStarter { public static void main(String[] args) { SpringApplication.run(GatewayStarter.class, args); log.info("start running gateway server : 【{}】", "http://127.0.0.1"); } }
GATEWAY配置类 package com.pet.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.client.RestTemplate; import java.nio.charset.StandardCharsets; @Configuration public class GatewayConfig { @Bean @LoadBalanced public RestTemplate restTemplate(ClientHttpRequestFactory factory) { RestTemplate restTemplate = new RestTemplate(factory); // 支持中文编码 restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); return restTemplate; } @Bean public ClientHttpRequestFactory simpleClientHttpRequestFactory() { HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(); httpRequestFactory.setReadTimeout(5000);// 单位为ms httpRequestFactory.setConnectTimeout(5000);// 单位为ms return httpRequestFactory; } }
GATEWAY配置文件

后面的服务通过CONFIG配置中心读取配置文件!此时配置文件名为bootstrap.yml! application.yml中啥也没有!

bootstrap.yml

server: port: 80 spring: application: name: gateway cloud: config: uri: http://localhost:8887 label: master name: gateway profile: dev discovery: enabled: true service-id: config fail-fast: true eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://EUREKA用户名:EUREKA密码@127.0.0.1:8888/eureka instance: prefer-ip-address: true ip-address: 127.0.0.1 instance-id: 127.0.0.1:${spring.application.name}:${server.port}

真正读取的配置文件:

server: port: 80 logging: file: path: D:\\LOG eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://EUREKA账号:EUREKA密码@127.0.0.1:8888/eureka instance: prefer-ip-address: true ip-address: 127.0.0.1 instance-id: 127.0.0.1:${spring.application.name}:${server.port} management: endpoints: web: exposure: include: "*" endpoint: health: show-details: ALWAYS spring: application: name: gateway jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 cloud: gateway: routes: # maintain服务路由 - id: maintain uri: lb://maintain predicates: - Path=/maintain/** filters: - StripPrefix=1 - name: Hystrix args: name: fallbackcmd fallbackUri: forward:/fallback/error # websocket服务路由 - id: websocket uri: lb://websocket predicates: - Path=/websocket/** filters: - StripPrefix=1 - name: Hystrix args: name: fallbackcmd fallbackUri: forward:/fallback/error1 # fallback服务路由 - id: fallback uri: lb://fallback predicates: - Path=/fallback/** # eureka服务路由 - id: eureka uri: http://127.0.0.1:8888 predicates: - Path=/eureka/** filters: - StripPrefix=1 # admin服务路由 - id: admin uri: lb://admin predicates: - Path=/admin/** filters: - StripPrefix=1 feign: hystrix: enabled: true hystrix: command: default: execution: isolation: thread: # 全局熔断器5s超时 timeoutInMilliseconds: 5000
启动GATEWAY服务

这里gateway用了80端口,因为里面配置了一个指向EUREKA的路由,所以此时可以通过访问 127.0.0.1/eureka 来测试是否成功(测试后可以删除EUREKA路由,没啥用😄) 出现下面页面,说明配置成功。


分别启动FALLBACK、MAINTAIN、WEBSOCKET服务
这三个服务没啥特别,配置文件和GATEWAY一样,读取对应的配置文件就好

也可以启动多个实例,启动多实例方法(先修改好git上的配置文件的端口!然后再配置IDEA!):


调用GATEWAY对应路由规则的MAINTAIN服务提供的接口测试

路由规则: FALLBACK服务提供的接口:

package com.pet.controller; import com.pet.config.ServiceException; import com.pet.constant.ErrorMsgConstant; import com.pet.vo.ResponseResultVO; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @RequestMapping("/fallback") public class FallBackController { @RequestMapping("/error") public ResponseEntity<ResponseResultVO<String>> fallback() { log.error(ErrorMsgConstant.MAINTAIN_ERROR); throw new ServiceException(ErrorMsgConstant.MAINTAIN_ERROR); } @RequestMapping("/error1") public ResponseEntity<ResponseResultVO<String>> fallback1() { log.error(ErrorMsgConstant.WEBSOCKET_ERROR); throw new ServiceException(ErrorMsgConstant.WEBSOCKET_ERROR); } }

测试成功情况: kill掉MATINTAIN服务,测试服务熔断: forward到fallback服务的接口,说明配置成功:


最后启动ADMIN服务
启动类 package com.pet; import de.codecentric.boot.admin.server.config.EnableAdminServer; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableDiscoveryClient @EnableAdminServer @Slf4j public class AdminStarter { public static void main(String[] args) { SpringApplication.run(AdminStarter.class, args); log.info("start running admin server"); } } 访问 127.0.0.1:8887 测试

出现下面页面,说明配置成功,具体作用大家自己探索把~


OpenFeign示例
WEBSOCKET服务接口

调用此方法可以推送websocket消息到页面。

package com.pet.controller; import com.pet.constant.RoomsConstant; import com.pet.event.entity.LogToDbEventEntity; import com.pet.servers.SystemInfoServer; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor public class PushLogController { private final SystemInfoServer systemInfoServer; @PostMapping("/pushLog") public void pushLogToWeb(@RequestBody LogToDbEventEntity log) { systemInfoServer.sendMsg(RoomsConstant.SYSTEM_INFO, log.toString()); } } MAINTAIN调用WEBSOCKET接口

package com.pet.service.manager; import com.pet.event.entity.LogToDbEventEntity; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @FeignClient(value = "websocket") public interface PushLogService { @PostMapping(value = "/pushLog", consumes = "application/json") void pushDateToWeb(@RequestBody LogToDbEventEntity log); }

MAINTAIN服务切面代码,在所用使用了@LogController注解的方法,调用WEBSOCKET服务,推送消息。

package com.pet.config.aop; import com.pet.annotation.LogController; import com.pet.config.ServiceException; import com.pet.constant.ErrorMsgConstant; import com.pet.constant.HttpConstant; import com.pet.event.entity.LogToDbEventEntity; import com.pet.po.SysUser; import com.pet.service.manager.PushLogService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.util.Date; import java.util.Objects; @RequiredArgsConstructor @Aspect @Component @Slf4j public class PushLogAspect { private final PushLogService pushLogService; @Pointcut("@annotation(com.pet.annotation.LogController)") public void pushPoint() { } @Before(value = "pushPoint() && @annotation(logController)", argNames = "joinPoint, logController") public void beforeController(JoinPoint joinPoint, LogController logController) { HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); HttpSession session = request.getSession(); SysUser user = (SysUser) session.getAttribute(HttpConstant.SESSION_USER); String realMethodName = joinPoint.getSignature().getName(); String requestIp = getIp(request); try { pushLogService.pushDateToWeb(LogToDbEventEntity.builder() .date(new Date()) .userName(Objects.isNull(user) ? "system" : user.getUserName()) .method(logController.method()) .logLevel(logController.logLevel()) .description(logController.description()) .realMethod(realMethodName) .ip(requestIp) .build()); } catch (Exception e) { throw new ServiceException(ErrorMsgConstant.WEBSOCKET_ERROR); } } public static String getIp(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-AuthenticationIp"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-AuthenticationIp"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } }
总结

大佬们如果clone下来可以用的话,给个免费的star吧~ 😄


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #基于springboot #Spring #Cloud的微服务框架