一、概念

基于springboot基础上用于快速构建分布式系统的通用模式的工具集。


二、特点

1、约定优于配置;

2、隐藏组件复杂性;

3、轻量级组件;

4、组件丰富,功能齐全,例如:服务发现、断路器、微服务网关等;

5、选型中立、丰富;

6、灵活。


三、服务消费者与服务提供者

在微服务架构中有两种角色:服务消费者与服务提供者,二者关系如下:

服务提供者:服务的被调用者

服务消费者:服务的调用者


例如,在电影系统中,用户购买电影票票之前,电影服务需要调用用户服务的接口获取用户信息,此时的电影服务就是调用方,即服务消费者,用户服务为被调用方,即服务提供者。


四、微服务实践

1、服务提供者:用户服务

项目结构如下:

这个demo主要是为了演示服务与服务之间的通信,因此不再配置数据源。

User.java

package com.my.user.entity;import lombok.Data;/** * @author 垃圾美少女 */@Datapublic class User {    private Integer id;    private String name;    private Integer age;    private String username;    private Integer balance;}

IUserService.java

package com.my.user.service;import com.my.user.entity.User;/** * @author 垃圾美少女 */public interface IUserService {    User getByUserId(Integer userId);}

UserServiceImpl.java

package com.my.user.service.impl;import com.my.user.entity.User;import com.my.user.service.IUserService;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;import java.util.Objects;import java.util.stream.Collectors;/** * @author 垃圾美少女 */@Servicepublic class UserServiceImpl implements IUserService {    @Override    public User getByUserId(Integer userId) {        List<User> userList = getUserList();        userList = userList.stream()                .filter(user -> Objects.equals(user.getId(), userId))                .collect(Collectors.toList());        if (userList != null && userList.size() > 0) {            return userList.get(0);        }        return null;    }    /**     * 由于没有配置数据源,在此设置虚拟数据     *     * @return list    */    private List<User> getUserList() {        List<User> list = new ArrayList<>(5);        for (int i = 0; i < 5; i++) {            User user = new User();            user.setAge(12 + 1);            user.setId(1 + i);            user.setName("用户" + i);            user.setUsername("用户名" + i);            user.setBalance(123 + i);            list.add(user);        }        return list;    }}

UserController.java

package com.my.user.controller;import com.my.user.Util.ReturnUtil;import com.my.user.entity.User;import com.my.user.service.IUserService;import lombok.extern.slf4j.Slf4j;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 java.util.Map;/** * @author 垃圾美少女 */@RestController@Slf4jpublic class UserController {    @Autowired    private IUserService userService;    /**     * 根据id获取用户信息     *     * @param userId 用户id     * @return map     */    @RequestMapping(value = "/user/getUserInfo", method = RequestMethod.GET)    public Map getUserInfo(Integer userId) {        try {            log.info("/user/getUserInfo被访问,参数:userId=" + userId);            User user = userService.getByUserId(userId);            return ReturnUtil.succe***esult(user, "获取成功");        } catch (Exception e) {            log.error(e.getMessage(), e);            return ReturnUtil.errorResult(null, "获取失败");        }    }}

ReturnUtil.java

package com.my.user.Util;import java.util.HashMap;import java.util.Map;/** * @author 垃圾美少女 */public class ReturnUtil {    public static Map succe***esult(Object data, String msg) {        Map<String, Object> map = new HashMap<>(3);        map.put("code", 1);        map.put("msg", msg);        map.put("data", data);        return map;    }    public static Map errorResult(Object data, String msg) {        Map<String, Object> map = new HashMap<>(3);        map.put("code", -1);        map.put("msg", msg);        map.put("data", data);        return map;    }}

application.yml

server:  port: 8010 #指定端口为  8010

启动项目后访问:http://localhost:8010/user/getUserInfo?userId=1

得到相应:

{    "msg": "获取成功",    "data": {        "id": 1,        "name": "用户0",        "age": 13,        "username": "用户名0",        "balance": 123    },    "code": 1}

表示接口已通。

2、服务消费者:电影服务

项目架构如下:

User.java和ReturnUtil.java与上例相同在此不再展示。


MovieApplicaiton.java

package com.my.movie;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;import org.springframework.web.client.RestTemplate;@SpringBootApplicationpublic class MovieApplication {    @Bean    public RestTemplate restTemplate() {        return new RestTemplate();    }    public static void main(String[] args) {        SpringApplication.run(MovieApplication.class, args);    }}

MovieController.java

package com.my.movie.controller;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.ResponseEntity;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.client.RestTemplate;import java.util.HashMap;import java.util.Map;/** * @author 垃圾美少女 */@RestController@Slf4jpublic class MovieController {    @Autowired    private RestTemplate restTemplate;    @RequestMapping(value = "/movie/findById", method = RequestMethod.GET)    public Map findById(Integer userId) {        log.info("/movie/findById被访问,参数:userId=" + userId);        ResponseEntity<HashMap> forEntity =                this.restTemplate.getForEntity("http://localhost:8010/user/getUserInfo?userId=" + userId, HashMap.class);        return forEntity.getBody();    }}

application.yml

server:  port: 8020

此时启动项目,访问:http://localhost:8020/movie/findById?userId=1

得到响应:

{    "msg": "获取成功",    "code": 1,    "data": {        "id": 1,        "name": "用户0",        "age": 13,        "username": "用户名0",        "balance": 123    }}

至此,一个简单的电影微服务就完成了。

五、上述例子中存在的问题

1、在代码中写死访问路径

在电影服务中,可以将user服务的访问路径写到yml配置文件中,使代码更清爽:

yml:

server:  port: 8020userService:  domain: http://localhost:8010/user/  getUserByIdUrl: http://localhost:8010/user/getUserInfo?userId=

MovieController.java

package com.my.movie.controller;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.http.ResponseEntity;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.client.RestTemplate;import java.util.HashMap;import java.util.Map;/** * @author 垃圾美少女 */@RestController@Slf4jpublic class MovieController {    @Autowired    private RestTemplate restTemplate;    @Value("${userService.domain}")    private String userServiceDomain;    @Value("${userService.getUserByIdUrl}")    private String findByUserIdUrl;    @RequestMapping(value = "/movie/findById", method = RequestMethod.GET)    public Map findById(Integer userId) {        log.info("/movie/findById被访问,参数:userId=" + userId);        ResponseEntity<HashMap> forEntity =                this.restTemplate.getForEntity(findByUserIdUrl + userId, HashMap.class);        return forEntity.getBody();    }}

2、适用场景有限:当用户服务的地址或端口号发生改变时,需要修改电影服务的配置文件并且重新部署,这显然是不可取的。