• springboot2 + grpc + k8s + istio


    项目情况说明:

    ubuntu - 16.04

    java - openjdk:8

    springboot - 2.2.2.RELEASE

    mysql - 5.7

    mongodb - 4.0.14

    redis - 3.0.6

    grpc - grpc-spring-boot-starter - 2.6.1.RELEASE

    项目目录:

    mypro - 

        device - 调用数据库,grpc客户端

        lib - 公共库

        person - 调用数据库,grpc服务端

    项目启动:

    步骤一:项目建立

    之所以从这里开始,因为我之前纠结过多模块项目的建立...

    IDE我使用的是IDEA

    1.1、建立主目录mypro

    新建文件夹mypro,在IDEA中依次选择:file -- New -- Project,弹出框选择Spring Initializr,点击Next。

    注意红框区域,选择Maven POM,其它可以自己填

     

    又弹出一个框来,什么也不选,直接Next

    选择地址(刚才新建的mypro文件夹)

    至此,新建主目录结束

    1.2、建立子目录

    右键单击主目录mypro,选择New,选择Module

    选择Spring Initializr,直接Next

    注意这里红框选择了Maven Project,其他可以自己填,填完Next

    便于测试,这里选择了Spring Web,点击Next,弹出框直接Next就创建成功了

    至此,第一个子模块建立成功,按照上面的步骤依次建立其它子模块,我这里建立了lib和person。

    1.3、调整pom配置

    主模块:去掉依赖,引入子模块,注意<packaging>pom</packaging>打包时会用到

     

    简单处理,把所有的依赖都由lib公共库引入,device和person模块引入lib公共库

    首先修改lib模块:<parent>使用了主模块的内容,直接复制过来即可,注意<packaging>jar</packaging>

    修改device模块:<parent>使用了主模块的内容,直接复制过来即可,注意<packaging>war</packaging>

    引入lib

    peron模块同上。

    至此,项目建立完成。

    步骤二:项目相关依赖

    1.1、mysql

    CREATE TABLE `user` (
      `name` char(16) NOT NULL,
      `age` int(11) DEFAULT NULL,
      `password` char(64) NOT NULL,
      `id` char(64) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    device | CREATE TABLE `device` (
      `id` char(64) NOT NULL,
      `name` char(16) NOT NULL,
      `code` char(32) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8

    使用的是mybatis-plus和代码生成器

         <dependency>
               <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
    
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.3.0</version>
            </dependency>
        
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.3.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>2.1</version>
            </dependency>

    代码生成器:(我放在test中,加注释的改成自己的就可以)

    import org.springframework.boot.test.context.SpringBootTest;
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.generator.AutoGenerator;
    import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
    import com.baomidou.mybatisplus.generator.config.GlobalConfig;
    import com.baomidou.mybatisplus.generator.config.PackageConfig;
    import com.baomidou.mybatisplus.generator.config.StrategyConfig;
    import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    import org.junit.jupiter.api.Test;
    
    @SpringBootTest
    public class CodeGenerator {
    
        @Test
        public void generateCode() {
            String packageName = "com.sam.person";  // 当前的包
            boolean serviceNameStartWithI = true;
            generateByTables(serviceNameStartWithI, packageName);
        }
    
        private void generateByTables(boolean serviceNameStartWithI, String packageName) {
            GlobalConfig config = new GlobalConfig();
    // 改成自己的 String dbUrl
    = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=UTC"; DataSourceConfig dataSourceConfig = new DataSourceConfig();
    // 用户名和密码 dataSourceConfig.setDbType(DbType.MYSQL).setUrl(dbUrl).setUsername(
    "root").setPassword("123456").setDriverName("com.mysql.cj.jdbc.Driver"); StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setCapitalMode(true).setEntityLombokModel(false) .setNaming(NamingStrategy.underline_to_camel)
          .setInclude(
    "user"); // 对应数据库表名,表要先创建 config.setActiveRecord(false).setAuthor("sam") // 作者 .setOutputDir("/home/sam/mypro/person/src/main/java") // 路径,到/java就可以 .setFileOverride(true) .setEnableCache(false); if (!serviceNameStartWithI) { config.setServiceName("%sService"); } new AutoGenerator() .setGlobalConfig(config) .setDataSource(dataSourceConfig) .setStrategy(strategyConfig) .setPackageInfo(new PackageConfig() .setParent(packageName) .setController("controller") .setEntity("entity.Do")) .execute(); } }

    不熟悉test的,可以放在如图这里,注意person有一个,device也要有一个,包和路径内容要调整下。

    生成的结构与下图类似:

    注意给启动类上加MapperScan: @MapperScan("com.sam.device.mapper"),person同理。

    没有config,是后加的,application.yml见后。

    开放远程连接:(重要)

    https://www.cnblogs.com/zhangkaiqiang/p/6646488.html

    1.2、mongodb

    可参考我另一篇随笔

    https://www.cnblogs.com/SamNicole1809/p/12097182.html

    开放远程连接:(重要)(参考,主要是需要设置bind-ip: 0.0.0.0,并重启服务)

    https://www.cnblogs.com/jinxiao-pu/p/7121307.html

    1.3、redis

    可参考我另一篇随笔

    https://www.cnblogs.com/SamNicole1809/p/12097440.html

    开放远程连接:(重要)

    https://blog.csdn.net/mr_oldcold/article/details/81026928

    1.4、yml配置

    黄色标注的根据自己情况填,没有可以不填,host填主机的Ip,不是localhost或127.0.0.1,部署时要用,这也是为什么要把所有数据库的远程连接都打开的原因。

    spring:
      datasource:
        username: root
        password: 123456
        url: jdbc:mysql://host:3306/test?useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=UTC
      redis:
        host: host
        password: 123456
        jedis:
          pool:
            max-active: 8
            max-idle: 8
            max-wait: -1ms
            min-idle: 0
      data:
        mongodb:
          uri: mongodb://sam:123456@host:27017/test

    至此,数据库部分结束。

    1.5、grpc

    请参考我另一篇随笔:(需要注意的是,device的yml在部署时需要修改address中的内容

    https://www.cnblogs.com/SamNicole1809/p/12201227.html

    1.6、测试controller

    device的:

    import com.sam.device.entity.Do.Device;
    import com.sam.device.service.grpc.DeviceGrpcService;
    import com.sam.device.service.impl.DeviceServiceImpl;
    import com.sam.lib.utils.MongoUtils;
    import com.sam.lib.utils.RedisUtils;
    import com.sam.lib.utils.StringUtils;
    import com.sam.lib.utils.TestUtils;
    import org.bson.Document;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.UUID;
    
    /**
     * <p>
     * 前端控制器
     * </p>
     *
     * @author sam
     * @since 2020-01-13
     */
    @RestController
    @RequestMapping("/device")
    public class DeviceController {
    
        private final TestUtils testUtils;
        private final DeviceServiceImpl deviceService;
        private final RedisUtils redisUtils;
        private final MongoUtils mongoUtils;
        private final DeviceGrpcService deviceGrpcService;
    
        public DeviceController(TestUtils testUtils, DeviceServiceImpl deviceService, RedisUtils redisUtils, MongoUtils mongoUtils, DeviceGrpcService deviceGrpcService) {
            this.testUtils = testUtils;
            this.deviceService = deviceService;
            this.redisUtils = redisUtils;
            this.mongoUtils = mongoUtils;
            this.deviceGrpcService = deviceGrpcService;
        }
    
        @GetMapping("/get")
        public String getDevice() {
            return StringUtils.getResult(testUtils.setLabel("Device"));
        }
    
        @GetMapping("/mysql")
        public String setMysql() {
            Device device = new Device();
            device.setId(UUID.randomUUID().toString());
            device.setCode("123456");
            device.setName("device-dev");
            boolean flag = deviceService.save(device);
            if (flag) {
                return device.toString();
            }
            return "Save device error";
        }
    
        @GetMapping("/redis")
        public String setRedis() {
            redisUtils.vSet("name", "device");
            String name = redisUtils.vGet("name").toString();
            return "Redis write success, name is " + name;
        }
    
        @GetMapping("/mongo")
        public String setMongo() {
            Document doc = new Document();
            String mid = UUID.randomUUID().toString();
            doc.put("mid", mid);
            doc.put("name", "device");
            mongoUtils.insertOne("device", doc);
            Document query = new Document();
            query.put("mid", mid);
            Document result = mongoUtils.findOne("device", query);
            return "Mongo write success, result is " + result.toString();
        }
    
        @GetMapping("/grpc")
        public String setGrpc() {
            String result = deviceGrpcService.getUser("device");
            if ("".equals(result)) {
                return "Result is blank";
            }
            return result;
        }
    }

    person的:

    import com.sam.lib.utils.MongoUtils;
    import com.sam.lib.utils.RedisUtils;
    import com.sam.lib.utils.StringUtils;
    import com.sam.lib.utils.TestUtils;
    import com.sam.person.entity.Do.User;
    import com.sam.person.service.impl.UserServiceImpl;
    import org.bson.Document;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.UUID;
    
    /**
     * <p>
     * 前端控制器
     * </p>
     *
     * @author sam
     * @since 2020-01-13
     */
    @RestController
    @RequestMapping("/user")
    public class UserController {
    
        private final TestUtils testUtils;
        private final UserServiceImpl userService;
        private final RedisUtils redisUtils;
        private final MongoUtils mongoUtils;
    
        public UserController(TestUtils testUtils, UserServiceImpl userService, RedisUtils redisUtils, MongoUtils mongoUtils) {
            this.testUtils = testUtils;
            this.userService = userService;
            this.redisUtils = redisUtils;
            this.mongoUtils = mongoUtils;
        }
    
        @GetMapping("/get")
        public String getUser() {
            return StringUtils.getResult(testUtils.setLabel("User"));
        }
    
        @GetMapping("/mysql")
        public String setMysql() {
            User user = new User();
            user.setId(UUID.randomUUID().toString());
            user.setName("sam");
            user.setAge(31);
            user.setPassword("123456");
            boolean flag = userService.save(user);
            if (flag) {
                return user.toString();
            }
            return "Save user error";
        }
    
        @GetMapping("/redis")
        public String setRedis() {
            redisUtils.vSet("name", "person");
            String name = redisUtils.vGet("name").toString();
            return "Redis write success, name is " + name;
        }
    
        @GetMapping("/mongo")
        public String setMongo() {
            Document doc = new Document();
            String mid = UUID.randomUUID().toString();
            doc.put("mid", mid);
            doc.put("name", "device");
            mongoUtils.insertOne("device", doc);
            Document query = new Document();
            query.put("mid", mid);
            Document result = mongoUtils.findOne("device", query);
            return "Mongo write success, result is " + result.toString();
        }
    }

    至此,依赖部分结束。

    步骤三:搭建k8s和istio环境

    请参考我另一篇随笔:

    https://www.cnblogs.com/SamNicole1809/p/12172887.html

    步骤四:项目打包

    1.1、跳过测试

    在所有子模块的pom文件中增加:<skipTests>true</skipTests>

        <properties>
            <java.version>1.8</java.version>
            <skipTests>true</skipTests>
        </properties>

    1.2、修改lib的pom

    注释掉原有的用于build,使用下面的build

        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <classifier>exec</classifier>
                    </configuration>
                </plugin>
            </plugins>
        </build>

    1.3、打包

    使用maven,一次执行mypro下的Lifecycle中的clean和package,会在person和device的target目录下生成war包,并自动处理关于lib的依赖问题。

    至此,项目打包完毕。

    步骤五:部署

    请确保已经参照了k8s和Istio的随笔,环境搭建成功,并能够正常运行,namespace: default开启了istio自动注入功能。

    可参考官方文档:

    https://preliminary.istio.io/zh/docs/examples/bookinfo/

    namespace: kube-system 

    namespace: istio-system

    1.1、生成docker镜像

    创建文件夹doc,将生成的war包复制到doc文件夹下,并创建文件Dockerfile,没有后缀,注意文件名必须这样

    Dockerfile文件内容:

    FROM openjdk:8-jdk-alpine
    VOLUME /tmp
    ARG device
    COPY device.war app.war
    ENTRYPOINT ["java","-Djava.security.egdod=file:/dev/./urandom","-jar","/app.war"]

    这里我把war包改名为device.war了,注意一下。

    person同理,将里面的device改为person就好。

    生成镜像命令:

    名称(黄色标记)建议不要更改,因为yaml文件会用到,以后可以自己改。

    docker build -t device-service .
    docker build -t person-service .

    1.2、添加yaml文件

    网关:mypro-gateway.yaml

    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: mypro-gateway
    spec:
      selector:
        istio: ingressgateway # use istio default controller
      servers:
        - port:
            number: 80
            name: http
            protocol: HTTP
          hosts:
            - "*"
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: mypro
    spec:
      hosts:
        - "*"
      gateways:
        - mypro-gateway
      http:
        - match:
            - uri:
                prefix: /user
          route:
            - destination:
                host: person
                port:
                  number: 8080
        - match:
            - uri:
                prefix: /device
          route:
            - destination:
                host: device
                port:
                  number: 8080

    deployment和service:mypro.yaml

    ##################################################################################################
    # person service
    ##################################################################################################
    apiVersion: v1
    kind: Service
    metadata:
      name: person
      labels:
        app: person
        service: person
    spec:
      ports:
        - name: http
          port: 8080
        - name: grpc
          port: 9898
          protocol: TCP
      selector:
        app: person
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: mypro-person
      labels:
        account: person
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: person-v1
      labels:
        app: person
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: person
          version: v1
      template:
        metadata:
          labels:
            app: person
            version: v1
        spec:
          serviceAccountName: mypro-person
          containers:
            - name: person
              image: person-service:latest
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 8080
                  name: http
                - containerPort: 9898
                  name: grpc
                  protocol: TCP
    ---
    ##################################################################################################
    # device service
    ##################################################################################################
    apiVersion: v1
    kind: Service
    metadata:
      name: device
      labels:
        app: device
        service: device
    spec:
      ports:
        - name: http
          port: 8080
        - name: grpc
          port: 9898
          protocol: TCP
      selector:
        app: device
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: mypro-device
      labels:
        account: device
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: device-v1
      labels:
        app: device
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: device
          version: v1
      template:
        metadata:
          labels:
            app: device
            version: v1
        spec:
          serviceAccountName: mypro-device
          containers:
            - name: device
              image: device-service:latest
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 8080
                  name: http
                - containerPort: 9898
                  name: grpc
                  protocol: TCP
    ---

    说明一下:

    1、9898是grpc端口,8080是person和device的默认端口

    2、device中的yml文件需要把address修改如下:(对应的服务名)

    address: 'static://person:9898'

    1.3、部署

    到yaml相关文件目录下执行如下命令:

    安装网关:

    kubectl apply -f mypro-gateway.yaml

    查看:

    设置url:(参考官方文档)

    https://preliminary.istio.io/zh/docs/tasks/traffic-management/ingress/ingress-control/#determining-the-ingress-i-p-and-ports

    echo GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT

    显示你能够访问的url地址,通过这个地址可以访问项目,正常是host:port都有。

    部署服务:

    kubectl apply -f mypro.yaml

    查看:

    至此项目部署成功。

    步骤五:测试

    http://host:port/device/get

    http://host:port/device/mysql 

    http://host:port/device/mongo

    http://host:port/device/redis

    http://host:port/device/grpc

    person相同

    至此,测试成功。

    结语:

    我想说的是:

    网络的目的在于分享,这个项目是在别人的经验上搭建起来的,我只是做了一个整合。

    本文的目的在于帮助刚接触k8s和istio的新人,少采坑。

    希望用微服务的人越来越多,把越来越多的资料经验分享出来。

    我是一个新手,希望文中有错误的大家请批评指正,以免影响更多的人。

    欢迎多多交流。

    Git地址:

    https://github.com/SamNicole1809/mypro

  • 相关阅读:
    JavaScript For, While和 递归
    adb 常用命令
    Android 测试 之adb shell
    Android测试入门学习
    Android 测试之Monkey
    Linux大全
    Android 测试 之MonkeyRunner
    手机耗电测试工具
    https双向认证网站搭建
    Mac下布置appium环境
  • 原文地址:https://www.cnblogs.com/SamNicole1809/p/12200875.html
Copyright © 2020-2023  润新知