第五階段day01

1.Spring Cloudweb

Spring Cloud是一個工具集,集成了多種工具,來解決微服務中的各類問題
微服務的總體解決方案 微服務全家桶spring

微服務治理,服務註冊和發現 eureka
負載均衡、請求重試 ribbon
斷路器,服務降級、熔斷 hystrix
ribbon + hystrix 集成,並提供聲明式客戶端 feign
hystrix 數據監控 hystrix dashboard 和 turbine
API 網關,提供微服務的統一入口,並提供統一的權限驗證 zuul
配置中心 config
消息總線, 配置刷新 bus
鏈路跟蹤 sleuth+zipkin
...json

Spring Cloud不是一個解決單一問題的框架!!!api

2.開發環境
IDEA或STS
Lombok
Mavenspringboot

3.Spring Cloud和Dubbo的區別服務器

image.png

Dubbo
1.Dubbo只是一個遠程調用(RPC)框架
2.默認基於長鏈接,支持多種序列化格式網絡

Spring Cloud
1.框架集
2.提供了一整套微服務解決方案(全家桶)
3.基於http調用, Rest APIapp

4.service- 服務負載均衡

image.png

五.item service商品服務
1.新建項目
2.配置依賴pom.xml 添加commons依賴
3.配置application.yml
image.png
4.配置主程序
5.編寫代碼框架

業務實現層
@Slf4j
@Service
public class ItemServiceImpl implements ItemService {

@Override
public List<Item> getItems(String orderId) {
    ArrayList<Item> list = new ArrayList<Item>();
    list.add(new Item(1, "商品 1",1));
    list.add(new Item(2, "商品 2",2));
    list.add(new Item(3, "商品 3",3));
    list.add(new Item(4, "商品 4",4));
    list.add(new Item(5, "商品 5",5));
    return list;
}

@Override
public void decreaseNumbers(List<Item> list) {
    for(Item item : list) {
        log.info("減小庫存 - "+item);
    }
}

}
控制層

@Slf4j
@RestController
public class ItemController {

@Autowired
private ItemService itemService;

@Value("${server.port}")
private int port;

@GetMapping("/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
    log.info("server.port="+port+", orderId="+orderId);
    
    List<Item> items = itemService.getItems(orderId);
    return JsonResult.ok(items).msg("port="+port);
}

@PostMapping("/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
    itemService.decreaseNumbers(items);
    return JsonResult.ok();
}

}

Spring MVC接收參數的幾個註解

@RequestParam
1.表單參數,鍵值對參數
2.能夠接受get或post請求提交的參數
3.name1=value&name2=value2&name3=value3
@PathVariable
1.請求路徑參數
2.http://localhost:8080/123
http://localhost:8080/123/aa/bb
@RequestBody
1.post請求協議體中的數據
2.完整的接受協議中的json數據

六.user service用戶服務
1.新建項目
2.配置依賴pom.xml
3.配置yml文件
image.png

4.配置主程序
5.編寫代碼

業務層

@Slf4j
@Service
public class UserServiceImpl implements UserService {

@Value("${sp.user-service.users}")
private String userJson;

@Override
public User getUser(Integer id) {
    log.info("users json string : "+userJson);
    List<User> list = JsonUtil.from(userJson, new TypeReference<List<User>>() {});
    for (User u : list) {
        if (u.getId().equals(id)) {
            return u;
        }
    }
    
    return new User(id, "name-"+id, "pwd-"+id);
}

@Override
public void addScore(Integer id, Integer score) {
    // 這裏增長積分
    log.info("user "+id+" - 增長積分 "+score);
}

}

控制層

@Slf4j
@RestController
public class UserController {

@Autowired
private UserService userService;

@GetMapping("/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId) {
    log.info("get user, userId="+userId);
    User u = userService.getUser(userId);
    return JsonResult.ok(u);
}

@GetMapping("/{userId}/score") 
public JsonResult addScore(
        @PathVariable Integer userId, Integer score) {
    userService.addScore(userId, score);
    return JsonResult.ok();
}

}

七. order service訂單服務

  1. 新建項目
  2. 配置依賴 pom.xml
  3. 配置 application.yml
    image.png
  4. 配置主程序
  5. 編寫代碼

業務層

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

@Override
public Order getOrder(String orderId) {
    //TODO: 調用user-service獲取用戶信息
    //TODO: 調用item-service獲取商品信息
    Order order = new Order();
    order.setId(orderId);
    return order;
}

@Override
public void addOrder(Order order) {
    //TODO: 調用item-service減小商品庫存
    //TODO: 調用user-service增長用戶積分
    log.info("保存訂單:"+order);
}

}

控制層

@Slf4j
@RestController
public class OrderController {

@Autowired
private OrderService orderService;

@GetMapping("/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId) {
    log.info("get order, id="+orderId);
    
    Order order = orderService.getOrder(orderId);
    return JsonResult.ok(order);
}

@GetMapping("/")
public JsonResult addOrder() {
    //模擬post提交的數據
    Order order = new Order();
    order.setId("123abc");
    order.setUser(new User(7,null,null));
    order.setItems(Arrays.asList(new Item[] {
            new Item(1,"aaa",2),
            new Item(2,"bbb",1),
            new Item(3,"ccc",3),
            new Item(4,"ddd",1),
            new Item(5,"eee",5),
    }));
    orderService.addOrder(order);
    return JsonResult.ok();
}

}

八 eureka 註冊與發現

image.png

  1. 建立eureka項目
  2. 配置依賴 pom.xml
  3. 配置 application.yml
    spring:
    application:
    name: eureka-server

server:
port: 2001

eureka:
server:

enable-self-preservation: false   關閉保護模式

instance:

hostname: eureka1     eureka集羣服務器之間 經過此標籤區分

client:

#不一樣自身註冊  不從自身拉取信息
register-with-eureka: false   
fetch-registry: false
  1. 主程序啓用 eureka 服務器
  2. 啓動,訪問測試

eureka註冊中心

做用:服務註冊和發現

提供者(Provider)
向註冊中心註冊本身的地址
消費者(Consumer)
從註冊中心發現其餘服務

eureka的運行機制

註冊 一次次反覆鏈接eureka 直到註冊成功爲止
拉取 每隔30秒拉取一次註冊表 更新註冊信息
心跳 每30秒發送一次心跳 3次收不到心跳 ureka會刪除這個服務
自我保護模式 特殊狀況 因爲網絡不穩定15分鐘內85%服務器出現心跳異常
保護全部的註冊信息不刪除
網絡恢復後 能夠自動退出保護模式
開發測試期間 能夠關閉保護模式

搭建eureka服務

1.eureka server依賴
2.yml文件配置
關閉保護模式
主機名(集羣中區分每臺服務器)
針對單臺服務器,不向本身註冊,不從本身拉取
3.啓動類
@EnableEurekaServer 觸發eureka服務器的自動配置

服務提供者

image.png

2.yml配置eureka鏈接地址

image.png

主程序添加@EnableEurekaServer註解

修改hosts文件

127.0.0.1 eureka1
127.0.0.1 eureka2

eureka和"服務提供者"的高可用(集羣)

image.png

遠程調用

RestTemplate

springboot 提供的遠程調用工具,相似 HttpClient

RestTemplate 對http Rest API 調用作了高度封裝,只須要調用一個方法就能夠完成請求、響應、json轉換

  • getForObject(url, 轉換的類型.class, 提交的參數數據)
  • postForObject(url, 提交的協議體數據, 轉換的類型.class)

用 RestTemplate 調用 2,3,4 項目

Ribbon

springcloud 提供的工具,對 RestTemplate 進行了加強封裝,提供了負載均衡和重試的功能

item-service 高可用

啓動參數 --server.port 能夠覆蓋yml中的端口配置

配置啓動參數
--server.port=8001
複製一份
--server.port=8002

eureka高可用

添加兩個服務器的profile配置文件

image.png

配置啓動參數和端口
--spring.profiles.active=eureka1 --server.port=2001
--spring.profiles.active=eureka2 --server.port=2002

eureka客戶端註冊時,向兩個服務器註冊

image.png

ribbon 服務消費者

image.png

ribbon 提供了負載均衡和重試功能 它底層是使用RestTemplate進行Rest api調用

RestTemplate

RestTemplate是SpringBoot提供的一個Rest遠程調用工具

經常使用方法
getForObject() 執行get請求
postForObject() 執行post請求

image.png

1.新建ribbon項目
2.pom.xml
添加Eureka Discovery Client 和 web 依賴
3.yml文件

spring:
application:

name: ribbon

server:
port: 3001

eureka:
client:

service-url:
  defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

4.主程序

@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {

//建立 RestTemplate 實例,並存入 spring 容器

@Bean
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}

public static void main(String[] args) {
    SpringApplication.run(Sp06RibbonApplication.class, args);
}

}
5.controller

@RestController
public class RibbonController {

@Autowired
private RestTemplate rt;

@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
    //向指定微服務地址發送 get 請求,並得到該服務的返回結果 
    //{1} 佔位符,用 orderId 填充
    return rt.getForObject("http://localhost:8001/{1}", JsonResult.class, orderId);
}

@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
    //發送 post 請求
    return rt.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class);
}

/

@GetMapping("/user-service/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId) {
    return rt.getForObject("http://localhost:8101/{1}", JsonResult.class, userId);
}

@GetMapping("/user-service/{userId}/score") 
public JsonResult addScore(
        @PathVariable Integer userId, Integer score) {
    return rt.getForObject("http://localhost:8101/{1}/score?score={2}", JsonResult.class, userId, score);
}

/

@GetMapping("/order-service/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId) {
    return rt.getForObject("http://localhost:8201/{1}", JsonResult.class, orderId);
}

@GetMapping("/order-service")
public JsonResult addOrder() {
    return rt.getForObject("http://localhost:8201/", JsonResult.class);
}

}
6.啓動 並訪問測試

ribbon負載均衡和測試

負載均衡

  1. 從 eureka 得到地址表
  2. 使用多個地址來回調用
  3. 拿到一個地址,使用 restTemplate 執行遠程調用

image.png

image.png

image.png

修改sp06-ribbon項目

1.添加ribbon起步依賴
2.RestTemplate設置@loadBalanced
3.訪問路徑設置爲服務id

@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {

@LoadBalanced //負載均衡註解
@Bean
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}

public static void main(String[] args) {
    SpringApplication.run(Sp06RibbonApplication.class, args);
}

}

訪問路徑設置問服務id

將控制層的本地地址和端口改成服務的id

例如 "http://item-service/{1}"

image.png

pom.xml文件中添加spring-retry依賴

<dependency>

<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>

</dependency>

yml文件配置ribbon重試

spring:
application:

name: ribbon

server:
port: 3001

eureka:
client:

service-url:
  defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

ribbon:
MaxAutoRetriesNextServer: 2 更換實例的次數
MaxAutoRetries: 1 當前實例重試次數 嘗試失敗會更換下一個實例
OkToRetryOnAllOperations: true 可不寫 默認支隊GET請求重試 默認爲false 當設置爲true時 對POST等全部的類型請求進行都重試

主程序設置RestTemplate的請求工廠的超時屬性

@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {

@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
    SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
    f.setConnectTimeout(1000);
    f.setReadTimeout(1000);
    return new RestTemplate(f);
    
    //RestTemplate 中默認的 Factory 實例中,兩個超時屬性默認是 -1,
    //未啓用超時,也不會觸發重試
    //return new RestTemplate();
}

public static void main(String[] args) {
    SpringApplication.run(Sp06RibbonApplication.class, args);
}

}

item-service 的 ItemController 添加延遲代碼,以便測試 ribbon 的重試機制

///--設置隨機延遲

if(Math.random()<0.6) { 
        long t = new Random().nextInt(5000);
        log.info("item-service-"+port+" - 暫停 "+t);
        Thread.sleep(t);
    }
相關文章
相關標籤/搜索