上文中已經講述了基本環境搭建,本文基於上文環境http://www.javashuo.com/article/p-ytxyscfm-dc.html。html
spring-cloud中微服務之間通訊主要有倆種形式:java
RestTempalte方式請求url硬編碼在客戶端,當有註冊中心有多個服務時,註冊中心沒法知道服務由誰提供。web
Feign方式因爲是以接口的形式進行通訊,更適合這種架構。spring
先來講RestTemplate方式,構建一個名爲custom-common的maven-project。apache
pom.xml:api
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.custom.mg</groupId> <artifactId>custom.root</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>custom.common</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> </project>
application.yml:安全
server: port: 8001 spring: application: name: custom-common #註冊中心指向start eureka: instance: instance-id: custom-common appname: ${spring.application.name} client: service-url: #url前面增長註冊中心帳號以及密碼 defaultZone: http://admin:123@127.0.0.1:8080/eureka/ #註冊中心指向end
編寫controller架構
package custom.common.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * * * @Title SayController.java * @Packge custom.common.controller * @Description TODO(用一句話描述該類的做用) * @Author Pandong * @Date 2019年2月14日 */ @RestController @RequestMapping(value="/common") public class SayController { @Value("${server.port}") private String port; @GetMapping(value="/hello") public String say() { return "Hello!!I'm server :"+port; } }
編寫入口類啓動註冊到註冊中心。app
再構建一個名爲custom-role的maven-project負載均衡
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.custom.mg</groupId> <artifactId>custom.root</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>custom.role</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> </project>
application.yml:
server: port: 8002 spring: application: name: custom-role #註冊中心指向start eureka: instance: instance-id: custom-role appname: ${spring.application.name} client: service-url: #url前面增長註冊中心帳號以及密碼 defaultZone: http://admin:123@127.0.0.1:8888/eureka/ #註冊中心指向end
注意:
這裏使用了defaultZone: http://admin:123@127.0.0.1:8888/eureka/。@前面的是對應註冊中心設置的帳號、密碼。
上篇章講述到訪問註冊中心會出現紅字提醒,是由於沒有爲註冊中心配置密碼,任何服務都能註冊進來,這裏將密碼配置進來,修改註冊中心的相關文件
——————————————————————————————————————————————————————————————————————————————————————
pom.xml中增長依賴:
<!-- 密碼配置依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
application.yml完整配置:
server: port: 8080 spring: security: user: name: admin # 配置登陸的帳號是admin password: 123 # 配置登陸的密碼是123 eureka: instance: hostname: localhost client: # 是否要註冊到其餘Eureka Server實例 register-with-eureka: false # 是否要從其餘Eureka Server實例獲取數據 fetch-registry: false service-url: defaultZone: http://{spring.security.user.name}:{spring.security.user.password}@{hostname}:{server.port}/eureka/ server: # 關閉註冊中心對服務的保護措施(若是服務掛掉了,又沒進行該配置,那麼服務會以UP狀態一直存在於註冊中心。反之會在3分鐘內容在註冊中心下線) enableSelfPreservation: false
增長安全策略配置:
package com.server.config; 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; /** * Spring Cloud Finchley及更高版本,必須添加以下代碼,部分關閉掉Spring Security的CSRF保護功能,不然應用沒法正常註冊! * {@link http://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_securing_the_eureka_server} * * @title SecurityConfig.java * @package com.server.config * @description TODO(一句話描述該類做用) * @author Pandong * @date 2019年2月18日 */ @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().ignoringAntMatchers("/eureka/**"); super.configure(http); } }
啓動註冊中心,訪問localhost:8080,會出現登陸頁面,輸入配置的帳號、密碼便可訪問,進去後會發現以前的紅字提醒不見了,可是,又出現新的紅字警示,這是由於application.yml中增長了enableSelfPreservation: false該項配置所引發,能夠不用理會。
——————————————————————————————————————————————————————————————————————————————————————
編寫新建工程入口類:
package com.user; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient public class RoleApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } }
編寫Controller:
package custom.role.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * * * @Title SayApiController.java * @Packge custom.role.controller * @Description TODO(用一句話描述該類的做用) * @Author Pandong * @Date 2019年2月14日 */ @RestController @RequestMapping(value="/api") public class SayApiController { @Value("${server.port}") private String port; @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; /** * 第一種方式,直接使用RestTemplate,url寫死。 * 由於服務端的 api 被硬編碼在客戶端,所以有兩個缺點: * – 當註冊中心有不少服務時,咱們可能不知道咱們須要的服務由誰提供、api是多少,所以就可能沒法調用到服務。 * –當某個服務部署了多個,例如 api 是: 「localhost:8080/hello,localhost:8081/hello 「,那麼此時就須要負載均衡,這種硬編碼顯然不符合場景。 * @return */ @GetMapping(value="/say") public String say() { RestTemplate template = new RestTemplate(); return template.getForObject("http://127.0.0.1:8001/common/hello", String.class); } /** * 第二種方式:客戶端經過 LoadBalancerClient 來獲取應用名,進而獲取地址和端口,在格式化拼接地址,從而調用 product服務。 * 缺點是每次調用服務都要這樣寫,編碼很麻煩。 */ @GetMapping("/say2") public String say2() { RestTemplate restTemplate = new RestTemplate(); ServiceInstance serviceInstance = loadBalancerClient.choose("custom-common"); // serviceId 爲提供服務的應用名 String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort() + "/common/hello"); String response = restTemplate.getForObject( url, String.class); return response; } /** * 第三種方式:經過 @LoadBalanced,可在restTemplate 直接使用應用名字。 * 這種方式須要編寫配置類,{@link custom.role.config.ConfigBeans} */ @GetMapping("/say3") public String say3() { return restTemplate.getForObject("http://custom-common/common/hello", String.class); } }
RestTemplate配置類
package custom.role.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class ConfigBeans { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
啓動服務,一次訪問,你會發現最後都會調用到custom-comon的服務中,這種硬編碼在代碼中的方式,顯然是不合適的。
下面來講使用Feign方式進行通訊。
在custom-role中添加依賴:
<!-- feign組件依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
入口類上面添加註解@EnableFeignClients。
而後編寫feign通訊接口:
package custom.role.service; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * 定義Feign接口 * @Title ServerService.java * @Packge custom.role.service * @Description TODO(用一句話描述該類的做用) * @Author Pandong * @Date 2019年2月14日 */ @FeignClient(name = "custom-common") // 調用服務名 public interface ServerService { @RequestMapping(method = RequestMethod.GET,value = "/common/hello") // 訪問路徑,要與服務中提供的一致 String hello(); }
@FeignClient(name = "custom-common"),custom-common對應的是服務提供者的服務名
編寫FeignController控制器:
package custom.role.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import custom.role.service.ServerService; /** * Feign通訊控制器 * @Title FeignClientController.java * @Packge custom.role.controller * @Description 以Feign方式進行服務之間的通訊 * @Author Pandong * @Date 2019年2月14日 */ @RestController @RequestMapping(value="/feign") public class FeignClientController { @Autowired private ServerService service; @RequestMapping(method = RequestMethod.GET,value = "/say") public String say() { return service.hello(); } }
方法上只能使用RequestMapping,不能使用GetMapping之類的註解。
啓動服務後訪問/feign/say,你會發現一樣會調用的custom-common中的服務。
相信到這裏你們對於選擇哪一種方式就不用多說了。
最後說一句,因爲在寫學習日記的時候是另一個版本,後面有從新搭建了一個更新的版本,文中都是從本地寫好的日記中拷貝的,有些地方對應不上基礎篇的地方就自行修改一下。